Skip to content

Commit 783ec7f

Browse files
authored
fix(pacmak): .NET bindings fail to compile with error CS8120 (#3760)
If a type union includes several candidates that are related to each other (A extends B or A implements B), `jsii-pacmak` may generate type checking clauses in a pattern matching `switch` statement in an order such that the compiler identifies dead clauses, which is an error in C#. This adds a provision to NOT emit such a clause so as to not cause the error. It is worth mentioning that the error cannot be "opted out" of via a `#pragma` directive like a warning would be, which is unfortunate. Fixes #3759 --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
1 parent a4d39c6 commit 783ec7f

File tree

14 files changed

+1596
-715
lines changed

14 files changed

+1596
-715
lines changed

.all-contributorsrc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,8 @@
12721272
"avatar_url": "https://avatars.githubusercontent.com/u/66279577?v=4",
12731273
"profile": "https://github.com/comcalvi",
12741274
"contributions": [
1275-
"code"
1275+
"code",
1276+
"review"
12761277
]
12771278
},
12781279
{
@@ -1428,6 +1429,34 @@
14281429
"contributions": [
14291430
"bug"
14301431
]
1432+
},
1433+
{
1434+
"login": "DanielMSchmidt",
1435+
"name": "Daniel Schmidt",
1436+
"avatar_url": "https://avatars.githubusercontent.com/u/1337046?v=4",
1437+
"profile": "http://danielmschmidt.de/",
1438+
"contributions": [
1439+
"bug",
1440+
"code"
1441+
]
1442+
},
1443+
{
1444+
"login": "kaizencc",
1445+
"name": "Kaizen Conroy",
1446+
"avatar_url": "https://avatars.githubusercontent.com/u/36202692?v=4",
1447+
"profile": "https://github.com/kaizencc",
1448+
"contributions": [
1449+
"code"
1450+
]
1451+
},
1452+
{
1453+
"login": "Naumel",
1454+
"name": "Naumel",
1455+
"avatar_url": "https://avatars.githubusercontent.com/u/104374999?v=4",
1456+
"profile": "https://github.com/Naumel",
1457+
"contributions": [
1458+
"review"
1459+
]
14311460
}
14321461
],
14331462
"repoType": "github",

README.md

Lines changed: 194 additions & 187 deletions
Large diffs are not rendered by default.

packages/jsii-calc/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export * as cdk16625 from './cdk16625';
2525
export * as jsii3656 from './jsii3656';
2626

2727
export * as anonymous from './anonymous';
28+
export * as union from './union';

packages/jsii-calc/lib/union.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { IFriendly } from '@scope/jsii-calc-lib';
2+
3+
export interface IResolvable {
4+
resolve(): any;
5+
}
6+
7+
export class Resolvable implements IResolvable {
8+
private constructor() {}
9+
10+
public resolve(): any {
11+
return false;
12+
}
13+
}
14+
15+
export class ConsumesUnion {
16+
public static unionType(param: IResolvable | Resolvable | IFriendly) {
17+
void param;
18+
}
19+
20+
private constructor() {}
21+
}

packages/jsii-calc/test/assembly.jsii

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,13 @@
421421
"line": 4
422422
},
423423
"symbolId": "lib/submodule/returns-param/index:"
424+
},
425+
"jsii-calc.union": {
426+
"locationInModule": {
427+
"filename": "lib/index.ts",
428+
"line": 28
429+
},
430+
"symbolId": "lib/union:"
424431
}
425432
},
426433
"targets": {
@@ -18199,8 +18206,125 @@
1819918206
"name": "ReturnsSpecialParameter",
1820018207
"namespace": "submodule.returnsparam",
1820118208
"symbolId": "lib/submodule/returns-param/index:ReturnsSpecialParameter"
18209+
},
18210+
"jsii-calc.union.ConsumesUnion": {
18211+
"assembly": "jsii-calc",
18212+
"docs": {
18213+
"stability": "stable"
18214+
},
18215+
"fqn": "jsii-calc.union.ConsumesUnion",
18216+
"kind": "class",
18217+
"locationInModule": {
18218+
"filename": "lib/union.ts",
18219+
"line": 15
18220+
},
18221+
"methods": [
18222+
{
18223+
"docs": {
18224+
"stability": "stable"
18225+
},
18226+
"locationInModule": {
18227+
"filename": "lib/union.ts",
18228+
"line": 16
18229+
},
18230+
"name": "unionType",
18231+
"parameters": [
18232+
{
18233+
"name": "param",
18234+
"type": {
18235+
"union": {
18236+
"types": [
18237+
{
18238+
"fqn": "jsii-calc.union.IResolvable"
18239+
},
18240+
{
18241+
"fqn": "jsii-calc.union.Resolvable"
18242+
},
18243+
{
18244+
"fqn": "@scope/jsii-calc-lib.IFriendly"
18245+
}
18246+
]
18247+
}
18248+
}
18249+
}
18250+
],
18251+
"static": true
18252+
}
18253+
],
18254+
"name": "ConsumesUnion",
18255+
"namespace": "union",
18256+
"symbolId": "lib/union:ConsumesUnion"
18257+
},
18258+
"jsii-calc.union.IResolvable": {
18259+
"assembly": "jsii-calc",
18260+
"docs": {
18261+
"stability": "stable"
18262+
},
18263+
"fqn": "jsii-calc.union.IResolvable",
18264+
"kind": "interface",
18265+
"locationInModule": {
18266+
"filename": "lib/union.ts",
18267+
"line": 3
18268+
},
18269+
"methods": [
18270+
{
18271+
"abstract": true,
18272+
"docs": {
18273+
"stability": "stable"
18274+
},
18275+
"locationInModule": {
18276+
"filename": "lib/union.ts",
18277+
"line": 4
18278+
},
18279+
"name": "resolve",
18280+
"returns": {
18281+
"type": {
18282+
"primitive": "any"
18283+
}
18284+
}
18285+
}
18286+
],
18287+
"name": "IResolvable",
18288+
"namespace": "union",
18289+
"symbolId": "lib/union:IResolvable"
18290+
},
18291+
"jsii-calc.union.Resolvable": {
18292+
"assembly": "jsii-calc",
18293+
"docs": {
18294+
"stability": "stable"
18295+
},
18296+
"fqn": "jsii-calc.union.Resolvable",
18297+
"interfaces": [
18298+
"jsii-calc.union.IResolvable"
18299+
],
18300+
"kind": "class",
18301+
"locationInModule": {
18302+
"filename": "lib/union.ts",
18303+
"line": 7
18304+
},
18305+
"methods": [
18306+
{
18307+
"docs": {
18308+
"stability": "stable"
18309+
},
18310+
"locationInModule": {
18311+
"filename": "lib/union.ts",
18312+
"line": 10
18313+
},
18314+
"name": "resolve",
18315+
"overrides": "jsii-calc.union.IResolvable",
18316+
"returns": {
18317+
"type": {
18318+
"primitive": "any"
18319+
}
18320+
}
18321+
}
18322+
],
18323+
"name": "Resolvable",
18324+
"namespace": "union",
18325+
"symbolId": "lib/union:Resolvable"
1820218326
}
1820318327
},
1820418328
"version": "3.20.120",
18205-
"fingerprint": "Ze43eowG9ImRufT3MQ8yO+bW8JzOQlZIYtFsjpc960E="
18329+
"fingerprint": "OaHwYmdPa8tbAJnlREahLGpRaNNBor2aoG04vsA/SvM="
1820618330
}

packages/jsii-pacmak/lib/targets/dotnet/runtime-type-checking.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,31 @@ abstract class Validation {
172172
code.openBlock(`switch (${expression})`);
173173
for (const type of types) {
174174
validTypes.push(resolver.toDotNetTypeName(type.spec!));
175+
176+
/**
177+
* Filter to remove classes and interfaces from a set of type references that
178+
* are implied by another entry in the set. Practically this is meant to remove
179+
* types from a set if a parent type of it is also present in the set, keeping
180+
* only the most generic declaration.
181+
*
182+
* This is useful because the TypeScript compiler and jsii do not guarantee that
183+
* all entries in a type union are unrelated, but the C# compiler treats dead
184+
* code as an error, and will refuse to compile (error CS8120) a pattern-matching
185+
* switch case if it cannot be matched (for example, if it matches on a child of
186+
* a type that was previously matched on already).
187+
*/
188+
if (
189+
(type.type?.isClassType() || type.type?.isInterfaceType()) &&
190+
types.some(
191+
(other) =>
192+
other !== type &&
193+
other.type != null &&
194+
type.type!.extends(other.type),
195+
)
196+
) {
197+
continue;
198+
}
199+
175200
const typeNames = [resolver.toDotNetType(type.spec!)];
176201
if (typeNames[0] === 'double') {
177202
// For doubles, we accept any numeric value, really...

0 commit comments

Comments
 (0)