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

Interfaces, Abstract Methods, and Virtual Overloading #862

Closed
wants to merge 52 commits into from

Conversation

willemneal
Copy link
Contributor

@willemneal willemneal commented Sep 22, 2019

  • Supports nominal and structural interfaces
  • Appropriate error messages for bad implementations
  • Handle interfaces with properties
  • Handle interfaces that extend interfaces
  • Handle abstract methods
  • Make virtual overloading default
  • Add @final decorator to prevent virtual method overloading.
  • Lookup uses a branch table
  • Add iterator/iterable interfaces
  • Add iterators to map.(values, keys, entries) and set.values and make them Iterable
  • handle interfaces that extend classes Not this PR

@willemneal
Copy link
Contributor Author

@MaxGraey How do I mark this as a draft?

@MaxGraey
Copy link
Member

MaxGraey commented Sep 22, 2019

I'm afraid it could only be done while setting/open PR at first time

@willemneal
Copy link
Contributor Author

Ah okay thanks I'll check that out next time. I've been using gitkraken to open the PR's so I'll switch to the github interface.

src/ast.ts Outdated Show resolved Hide resolved
@willemneal
Copy link
Contributor Author

@MaxGraey and @dcodeIO My new commits are not being tested on travis; do you of any reason why? Have we made the switch to github Actions?

src/ast.ts Outdated Show resolved Hide resolved
src/ast.ts Outdated Show resolved Hide resolved
Soon to add iterator
src/program.ts Outdated Show resolved Hide resolved
src/program.ts Outdated Show resolved Hide resolved
src/program.ts Outdated Show resolved Hide resolved
src/program.ts Outdated
@@ -3408,6 +3438,9 @@ export class Class extends TypedElement {
/** Tests if a value of this class type is assignable to a target of the specified class type. */
isAssignableTo(target: Class): bool {
var current: Class | null = this;
if (target.kind == ElementKind.INTERFACE) {
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because checking the interface requires resolving function prototypes. Which is why the check happens in the compiler method. This was an artifact as the condition in the compiler's method checks if the target is an interface so this never actually gets triggered. That is my least favorite part of this solution since it should happen in this method.

addImplementer(_class: Class): void {
this.implementers.add(_class);
if (this.base != null) {
(<Class>this.base).addImplementer(_class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if base is null, should that maybe hit an assertion?

Copy link
Contributor Author

@willemneal willemneal Dec 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A class on interface that doesn't extend anything has a base that is null. So it's the "base case" in the recursion. Should we instead make an Object class the default ?

src/program.ts Outdated Show resolved Hide resolved
iDecl.typeParameters = cDecl.typeParameters;
iDecl.extendsType = cDecl.extendsType;
iDecl.implementsTypes = cDecl.implementsTypes;
iDecl.members = cDecl.members;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like there's more to this than these properties, like the source range

src/program.ts Outdated
typeArguments: Type[] = [],
base: Interface | null = null
typeArguments: Type[] | null = [],
base: Interface | Class | null = null // interface can extend classes in typescript
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no union types when compiling this as AS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it should just be class.

src/program.ts Outdated Show resolved Hide resolved

export function notNull<T>(t: T): bool {
return t != null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of these, as it's trading convenience for the overhead of indirect calls. Would prefer to inline.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I never end up using these, did at first for the convenience but removed most used of iterators after first batch of feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I never ended up using these after removing most of my use of iterations in my first iteration. I'll remove them.

src/compiler.ts Outdated Show resolved Hide resolved
src/compiler.ts Outdated
// this.error(
// DiagnosticCode.Operation_not_supported,
// declaration.range
// );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there missing something here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this could be used to merge multiple interface declarations.

DiagnosticCode.Type_0_is_not_assignable_to_type_1,
reportNode.range,
fromType.toString(),
toType.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

src/compiler.ts Outdated Show resolved Hide resolved
@@ -9217,12 +9280,394 @@ export class Compiler extends DiagnosticEmitter {
return module.block(label, conditions, NativeType.I32);
}

checkInterfaceImplementation(toType: Type, fromType: Type, reportNode: Node): bool {
const _interface = (<Interface>toType.classReference!);
const _class = fromType.classReference!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these should be assert calls

DiagnosticCode.Type_0_is_not_assignable_to_type_1,
(<DeclaredElement>mem).declaration.range,
mem.name,
name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation; also looks like the error won't be very useful as it doesn't tell what's actually wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just went off the compiler errors I got from typescript. In fact this should result in a parsing error, so I'll look into that.

name);
continue;
}
// Error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something missing here?

src/compiler.ts Outdated
mem = (<Field>mem).prototype;
imem = (<Property>imem).prototype;
}
case ElementKind.FIELD_PROTOTYPE: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixing prototypes and instances here seems odd, like if it's missing a distinction of static vs instance.

src/compiler.ts Outdated
}
incorrectMember = false;
}
error = error || incorrectMember;// case where contiune skips the last check.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling

src/compiler.ts Outdated
_class.identifierNode.range,
fromType.toString(),
toType.toString()
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

relooper.addBranchForSwitch(first, returnBlock, classIds);
}

// finish relooper and prepare body of function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if the relooper will potentially build huge br_tables here, for instance when dealing with ids 1 to 1024 - do we get 1024 cases? Other code intentionally generates sequences of br_ifs so Binaryen can decide if it's worth to make a br_table or leave these as is depending on optimize and shrink levels.

src/compiler.ts Outdated Show resolved Hide resolved
@dcodeIO
Copy link
Member

dcodeIO commented Dec 17, 2019

Overall this appears somewhat rushed to me, with various minor problems, some oversights and missing comments to understand why it's built exactly this way. Don't get me wrong please, I appreciate your efforts, but I have a feeling that getting this right will be hard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants