Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support class member type infer #2794

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/ast.ts
Original file line number Diff line number Diff line change
@@ -49,6 +49,7 @@ export const enum NodeKind {
Source,

// types
ResolvedType,
NamedType,
FunctionType,
TypeName,
@@ -168,6 +169,10 @@ export abstract class Node {
return new NamedTypeNode(Node.createSimpleTypeName("", range), null, false, range);
}

static createResolvedType(type: Type): ResolvedType {
return new ResolvedType(type);
}

static createTypeParameter(
name: IdentifierExpression,
extendsType: NamedTypeNode | null,
@@ -863,13 +868,24 @@ export abstract class TypeNode extends Node {
if (functionTypeNode.returnType.hasGenericComponent(typeParameterNodes)) return true;
let explicitThisType = functionTypeNode.explicitThisType;
if (explicitThisType && explicitThisType.hasGenericComponent(typeParameterNodes)) return true;
} else if (this.kind == NodeKind.ResolvedType) {
return false;
} else {
assert(false);
}
return false;
}
}

/** Represents a resolved type. Will be generated when */
export class ResolvedType extends TypeNode {
constructor(
public type: Type
) {
super(NodeKind.ResolvedType, false, Source.native.range);
}
}

/** Represents a type name. */
export class TypeName extends Node {
constructor(
5 changes: 0 additions & 5 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -2379,11 +2379,6 @@ export class Parser extends DiagnosticEmitter {
if (tn.skip(Token.Colon)) {
type = this.parseType(tn);
if (!type) return null;
} else {
this.error(
DiagnosticCode.Type_expected,
tn.range()
); // recoverable
}
let initializer: Expression | null = null;
if (tn.skip(Token.Equals)) {
24 changes: 22 additions & 2 deletions src/resolver.ts
Original file line number Diff line number Diff line change
@@ -76,7 +76,8 @@ import {
NewExpression,
ArrayLiteralExpression,
ArrowKind,
ExpressionStatement
ExpressionStatement,
ResolvedType
} from "./ast";

import {
@@ -159,6 +160,9 @@ export class Resolver extends DiagnosticEmitter {
node.currentlyResolving = true;
let resolved: Type | null = null;
switch (node.kind) {
case NodeKind.ResolvedType:
resolved = (<ResolvedType>node).type;
break;
case NodeKind.NamedType: {
resolved = this.resolveNamedType(
<NamedTypeNode>node,
@@ -3658,6 +3662,23 @@ export class Resolver extends DiagnosticEmitter {
prototype.parent // same level as prototype
);
let getterPrototype = prototype.getterPrototype;
let setterPrototype = prototype.setterPrototype;

let initializerNode = prototype.initializerNode;
if (initializerNode != null && prototype.typeNode == null) {
let initialzerType = this.resolveExpression(initializerNode, instance.file.startFunction.flow);
if (initialzerType) {
if (getterPrototype) {
assert(isTypeOmitted(getterPrototype.functionTypeNode.returnType));
getterPrototype.functionTypeNode.returnType = Node.createResolvedType(initialzerType);
}
if (setterPrototype) {
assert(isTypeOmitted(setterPrototype.functionTypeNode.parameters[0].type));
setterPrototype.functionTypeNode.parameters[0].type = Node.createResolvedType(initialzerType);
}
}
}

if (getterPrototype) {
let getterInstance = this.resolveFunction(
getterPrototype,
@@ -3670,7 +3691,6 @@ export class Resolver extends DiagnosticEmitter {
instance.setType(getterInstance.signature.returnType);
}
}
let setterPrototype = prototype.setterPrototype;
if (setterPrototype) {
let setterInstance = this.resolveFunction(
setterPrototype,
657 changes: 559 additions & 98 deletions tests/compiler/class.debug.wat

Large diffs are not rendered by default.

717 changes: 549 additions & 168 deletions tests/compiler/class.release.wat

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions tests/compiler/class.ts
Original file line number Diff line number Diff line change
@@ -40,3 +40,16 @@ class GenericInitializer<T> {
export function testGenericInitializer(): void {
new GenericInitializer<i32>();
}


let outsideF32: f32 = 1.2;
class MemberTypeInfer {
a = 1;
b = outsideF32;
c = "hhh";
}

let memberTypeInfer = new MemberTypeInfer();
assert(nameof(memberTypeInfer.a) == nameof<i32>());
assert(nameof(memberTypeInfer.b) == nameof<f32>());
assert(nameof(memberTypeInfer.c) == nameof<string>());
6 changes: 6 additions & 0 deletions tests/parser/class.ts
Original file line number Diff line number Diff line change
@@ -41,3 +41,9 @@ export class Invalid<T> {
// ERROR 1042: "'override' modifier cannot be used here."
override overrideMethod(): void {}
}

export class MemberTypeInfer {
a = 1;
b = 1.2;
c = "hhh";
}
5 changes: 5 additions & 0 deletions tests/parser/class.ts.fixture.ts
Original file line number Diff line number Diff line change
@@ -19,6 +19,11 @@ export class Invalid<T> {
declare declareMethod(): i32 {}
overrideMethod(): void {}
}
export class MemberTypeInfer {
a = 1;
b = 1.2;
c = "hhh";
}
// ERROR 1092: "Type parameters cannot appear on a constructor declaration." in class.ts(15,14+3)
// ERROR 1110: "Type expected." in class.ts(18,21+0)
// ERROR 1094: "An accessor cannot have type parameters." in class.ts(23,21+3)