mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Basic initial inheritance
This commit is contained in:
parent
ae99adefce
commit
50dea3b1df
@ -352,8 +352,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return false;
|
||||
} else if (declaration.initializer) {
|
||||
initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable
|
||||
if (this.currentType == Type.void)
|
||||
if (this.currentType == Type.void) {
|
||||
this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.range, this.currentType.toString(), "<auto>");
|
||||
return false;
|
||||
}
|
||||
global.type = this.currentType;
|
||||
} else {
|
||||
this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd);
|
||||
@ -1010,8 +1012,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
init = this.compileExpression(declaration.initializer, type); // reports and returns unreachable
|
||||
} else if (declaration.initializer) { // infer type
|
||||
init = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable
|
||||
if ((type = this.currentType) == Type.void)
|
||||
if (this.currentType == Type.void) {
|
||||
this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.range, this.currentType.toString(), "<auto>");
|
||||
continue;
|
||||
}
|
||||
type = this.currentType;
|
||||
} else {
|
||||
this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd);
|
||||
continue;
|
||||
@ -1168,8 +1173,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// void to any
|
||||
if (fromType.kind == TypeKind.VOID) {
|
||||
this.error(DiagnosticCode.Operation_not_supported, reportNode.range);
|
||||
throw new Error("unexpected conversion from void");
|
||||
this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, reportNode.range, fromType.toString(), toType.toString());
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
// any to void
|
||||
@ -2108,6 +2113,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (!resolved)
|
||||
return this.module.createUnreachable();
|
||||
|
||||
// to compile just the value, we need to know the target's type
|
||||
var element = resolved.element;
|
||||
var elementType: Type;
|
||||
switch (element.kind) {
|
||||
@ -2143,9 +2149,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
if (!elementType)
|
||||
return this.module.createUnreachable();
|
||||
|
||||
// now compile the value and do the assignment
|
||||
this.currentType = elementType;
|
||||
return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, elementType, ConversionKind.IMPLICIT), contextualType != Type.void);
|
||||
}
|
||||
@ -2208,25 +2213,29 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (setterPrototype) {
|
||||
var setterInstance = setterPrototype.resolve(); // reports
|
||||
if (setterInstance) {
|
||||
if (!tee)
|
||||
assert(setterInstance.parameters.length == 1);
|
||||
if (!tee) {
|
||||
this.currentType = Type.void;
|
||||
return this.makeCall(setterInstance, [ valueWithCorrectType ]);
|
||||
}
|
||||
var getterPrototype = (<Property>element).getterPrototype;
|
||||
assert(getterPrototype != null);
|
||||
var getterInstance = (<FunctionPrototype>getterPrototype).resolve(); // reports
|
||||
if (getterInstance)
|
||||
if (getterInstance) {
|
||||
assert(getterInstance.parameters.length == 0);
|
||||
this.currentType = getterInstance.returnType;
|
||||
return this.module.createBlock(null, [
|
||||
this.makeCall(setterInstance, [ valueWithCorrectType ]),
|
||||
this.makeCall(getterInstance)
|
||||
], getterInstance.returnType.toNativeType());
|
||||
}
|
||||
}
|
||||
} else
|
||||
this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Property>element).internalName);
|
||||
return this.module.createUnreachable();
|
||||
|
||||
default:
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef {
|
||||
@ -2527,7 +2536,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var getterInstance = (<FunctionPrototype>getter).resolve(); // reports
|
||||
if (!getterInstance)
|
||||
return this.module.createUnreachable();
|
||||
return this.compileCall(getterInstance, [], propertyAccess);
|
||||
assert(getterInstance.parameters.length == 0);
|
||||
this.currentType = getterInstance.returnType;
|
||||
return this.makeCall(getterInstance);
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, propertyAccess.range);
|
||||
return this.module.createUnreachable();
|
||||
|
@ -8,6 +8,8 @@ export enum DiagnosticCode {
|
||||
Cannot_export_a_mutable_global = 104,
|
||||
Compiling_constant_with_non_constant_initializer_as_mutable = 105,
|
||||
Type_0_cannot_be_changed_to_type_1 = 106,
|
||||
Structs_cannot_extend_classes_and_vice_versa = 107,
|
||||
Structs_cannot_implement_interfaces = 108,
|
||||
Unterminated_string_literal = 1002,
|
||||
Identifier_expected = 1003,
|
||||
_0_expected = 1005,
|
||||
@ -54,6 +56,7 @@ export enum DiagnosticCode {
|
||||
Unterminated_Unicode_escape_sequence = 1199,
|
||||
Decorators_are_not_valid_here = 1206,
|
||||
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
|
||||
A_class_may_only_extend_another_class = 1311,
|
||||
Duplicate_identifier_0 = 2300,
|
||||
Cannot_find_name_0 = 2304,
|
||||
Module_0_has_no_exported_member_1 = 2305,
|
||||
@ -89,6 +92,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 104: return "Cannot export a mutable global.";
|
||||
case 105: return "Compiling constant with non-constant initializer as mutable.";
|
||||
case 106: return "Type '{0}' cannot be changed to type '{1}'.";
|
||||
case 107: return "Structs cannot extend classes and vice-versa.";
|
||||
case 108: return "Structs cannot implement interfaces.";
|
||||
case 1002: return "Unterminated string literal.";
|
||||
case 1003: return "Identifier expected.";
|
||||
case 1005: return "'{0}' expected.";
|
||||
@ -135,6 +140,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1199: return "Unterminated Unicode escape sequence.";
|
||||
case 1206: return "Decorators are not valid here.";
|
||||
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
|
||||
case 1311: return "A class may only extend another class.";
|
||||
case 2300: return "Duplicate identifier '{0}'.";
|
||||
case 2304: return "Cannot find name '{0}'.";
|
||||
case 2305: return "Module '{0}' has no exported member '{1}'.";
|
||||
|
@ -6,6 +6,8 @@
|
||||
"Cannot export a mutable global.": 104,
|
||||
"Compiling constant with non-constant initializer as mutable.": 105,
|
||||
"Type '{0}' cannot be changed to type '{1}'.": 106,
|
||||
"Structs cannot extend classes and vice-versa.": 107,
|
||||
"Structs cannot implement interfaces.": 108,
|
||||
|
||||
"Unterminated string literal.": 1002,
|
||||
"Identifier expected.": 1003,
|
||||
@ -53,6 +55,7 @@
|
||||
"Unterminated Unicode escape sequence.": 1199,
|
||||
"Decorators are not valid here.": 1206,
|
||||
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
|
||||
"A class may only extend another class.": 1311,
|
||||
|
||||
"Duplicate identifier '{0}'.": 2300,
|
||||
"Cannot find name '{0}'.": 2304,
|
||||
|
@ -265,6 +265,12 @@ export class Program extends DiagnosticEmitter {
|
||||
else
|
||||
this.elements.set(declaration.name.name, prototype);
|
||||
}
|
||||
if (hasDecorator("struct", declaration.decorators)) {
|
||||
prototype.isStruct = true;
|
||||
if (declaration.implementsTypes && declaration.implementsTypes.length)
|
||||
this.error(DiagnosticCode.Structs_cannot_implement_interfaces, Range.join(declaration.name.range, declaration.implementsTypes[declaration.implementsTypes.length - 1].range));
|
||||
} else if (declaration.implementsTypes.length)
|
||||
throw new Error("not implemented");
|
||||
|
||||
// add as namespace member if applicable
|
||||
if (namespace) {
|
||||
@ -377,6 +383,9 @@ export class Program extends DiagnosticEmitter {
|
||||
} else
|
||||
classPrototype.instanceMembers = new Map();
|
||||
var instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
|
||||
// if (classPrototype.isStruct && instancePrototype.isAbstract) {
|
||||
// this.error( Structs cannot declare abstract methods. );
|
||||
// }
|
||||
classPrototype.instanceMembers.set(name, instancePrototype);
|
||||
}
|
||||
}
|
||||
@ -946,10 +955,10 @@ export class Program extends DiagnosticEmitter {
|
||||
case ElementKind.GLOBAL:
|
||||
case ElementKind.LOCAL:
|
||||
targetType = (<VariableLikeElement>target).type;
|
||||
if (!targetType) // FIXME: are globals always resolved here?
|
||||
throw new Error("type expected");
|
||||
if (targetType.classType)
|
||||
target = targetType.classType;
|
||||
assert(targetType != null); // FIXME: this is a problem because auto-globals might not be
|
||||
// resolved (and should not be attempted to be resolved) here
|
||||
if ((<Type>targetType).classType)
|
||||
target = <Class>(<Type>targetType).classType;
|
||||
// fall-through
|
||||
else
|
||||
break;
|
||||
@ -1104,7 +1113,11 @@ export enum ElementFlags {
|
||||
/** Is a protected member. */
|
||||
PROTECTED = 1 << 14,
|
||||
/** Is a private member. */
|
||||
PRIVATE = 1 << 15
|
||||
PRIVATE = 1 << 15,
|
||||
/** Is an abstract member. */
|
||||
ABSTRACT = 1 << 16,
|
||||
/** Is a struct-like class with limited capabilites. */
|
||||
STRUCT = 1 << 17
|
||||
}
|
||||
|
||||
/** Base class of all program elements. */
|
||||
@ -1779,7 +1792,11 @@ export class ClassPrototype extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
resolve(typeArguments: Type[] | null, contextualTypeArguments: Map<string,Type> | null = null): Class {
|
||||
/** Whether a struct-like class with limited capabilities or not. */
|
||||
get isStruct(): bool { return (this.flags & ElementFlags.STRUCT) != 0; }
|
||||
set isStruct(is: bool) { if (is) this.flags |= ElementFlags.STRUCT; else this.flags &= ~ElementFlags.STRUCT; }
|
||||
|
||||
resolve(typeArguments: Type[] | null, contextualTypeArguments: Map<string,Type> | null = null): Class | null {
|
||||
var instanceKey = typeArguments ? typesToString(typeArguments) : "";
|
||||
var instance = this.instances.get(instanceKey);
|
||||
if (instance)
|
||||
@ -1796,8 +1813,20 @@ export class ClassPrototype extends Element {
|
||||
for (var [inheritedName, inheritedType] of inheritedTypeArguments)
|
||||
contextualTypeArguments.set(inheritedName, inheritedType);
|
||||
|
||||
if (declaration.extendsType) // TODO: base class
|
||||
throw new Error("not implemented");
|
||||
var baseClass: Class | null = null;
|
||||
if (declaration.extendsType) {
|
||||
var baseClassType = this.program.resolveType(declaration.extendsType, null); // reports
|
||||
if (!baseClassType)
|
||||
return null;
|
||||
if (!(baseClass = baseClassType.classType)) {
|
||||
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
|
||||
return null;
|
||||
}
|
||||
if (baseClass.prototype.isStruct != this.isStruct) {
|
||||
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// override call specific contextual type arguments if provided
|
||||
var i: i32, k: i32;
|
||||
@ -1812,11 +1841,20 @@ export class ClassPrototype extends Element {
|
||||
var internalName = this.internalName;
|
||||
if (instanceKey.length)
|
||||
internalName += "<" + instanceKey + ">";
|
||||
instance = new Class(this, internalName, typeArguments, null); // TODO: base class
|
||||
instance = new Class(this, internalName, typeArguments, baseClass);
|
||||
instance.contextualTypeArguments = contextualTypeArguments;
|
||||
this.instances.set(instanceKey, instance);
|
||||
|
||||
var memoryOffset: i32 = 0;
|
||||
if (baseClass) {
|
||||
memoryOffset = baseClass.type.byteSize;
|
||||
if (baseClass.members) {
|
||||
if (!instance.members)
|
||||
instance.members = new Map();
|
||||
for (var inheritedMember of baseClass.members.values())
|
||||
instance.members.set(inheritedMember.simpleName, inheritedMember);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.instanceMembers)
|
||||
for (var member of this.instanceMembers.values()) {
|
||||
|
26
tests/compiler/class-extends.optimized.wast
Normal file
26
tests/compiler/class-extends.optimized.wast
Normal file
@ -0,0 +1,26 @@
|
||||
(module
|
||||
(type $iv (func (param i32)))
|
||||
(memory $0 1)
|
||||
(export "test" (func $class-extends/test))
|
||||
(export "memory" (memory $0))
|
||||
(func $class-extends/test (; 0 ;) (type $iv) (param $0 i32)
|
||||
(drop
|
||||
(i32.load
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(drop
|
||||
(i32.load16_s offset=4
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.const 2)
|
||||
)
|
||||
(i32.store16 offset=4
|
||||
(get_local $0)
|
||||
(i32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
14
tests/compiler/class-extends.ts
Normal file
14
tests/compiler/class-extends.ts
Normal file
@ -0,0 +1,14 @@
|
||||
class A {
|
||||
a: i32 = 0;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
b: i16 = 1;
|
||||
}
|
||||
|
||||
export function test(b: B): void {
|
||||
b.a;
|
||||
b.b;
|
||||
b.a = 2;
|
||||
b.b = 3;
|
||||
}
|
79
tests/compiler/class-extends.wast
Normal file
79
tests/compiler/class-extends.wast
Normal file
@ -0,0 +1,79 @@
|
||||
(module
|
||||
(type $iv (func (param i32)))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "test" (func $class-extends/test))
|
||||
(export "memory" (memory $0))
|
||||
(func $class-extends/test (; 0 ;) (type $iv) (param $0 i32)
|
||||
(drop
|
||||
(i32.load
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(drop
|
||||
(i32.load16_s offset=4
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.const 2)
|
||||
)
|
||||
(i32.store16 offset=4
|
||||
(get_local $0)
|
||||
(i32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
||||
(;
|
||||
[program.elements]
|
||||
GLOBAL: NaN
|
||||
GLOBAL: Infinity
|
||||
FUNCTION_PROTOTYPE: isNaN
|
||||
FUNCTION_PROTOTYPE: isFinite
|
||||
FUNCTION_PROTOTYPE: clz
|
||||
FUNCTION_PROTOTYPE: ctz
|
||||
FUNCTION_PROTOTYPE: popcnt
|
||||
FUNCTION_PROTOTYPE: rotl
|
||||
FUNCTION_PROTOTYPE: rotr
|
||||
FUNCTION_PROTOTYPE: abs
|
||||
FUNCTION_PROTOTYPE: max
|
||||
FUNCTION_PROTOTYPE: min
|
||||
FUNCTION_PROTOTYPE: ceil
|
||||
FUNCTION_PROTOTYPE: floor
|
||||
FUNCTION_PROTOTYPE: copysign
|
||||
FUNCTION_PROTOTYPE: nearest
|
||||
FUNCTION_PROTOTYPE: reinterpret
|
||||
FUNCTION_PROTOTYPE: sqrt
|
||||
FUNCTION_PROTOTYPE: trunc
|
||||
FUNCTION_PROTOTYPE: load
|
||||
FUNCTION_PROTOTYPE: store
|
||||
FUNCTION_PROTOTYPE: sizeof
|
||||
FUNCTION_PROTOTYPE: select
|
||||
FUNCTION_PROTOTYPE: unreachable
|
||||
FUNCTION_PROTOTYPE: current_memory
|
||||
FUNCTION_PROTOTYPE: grow_memory
|
||||
FUNCTION_PROTOTYPE: parseInt
|
||||
FUNCTION_PROTOTYPE: parseFloat
|
||||
FUNCTION_PROTOTYPE: changetype
|
||||
FUNCTION_PROTOTYPE: assert
|
||||
FUNCTION_PROTOTYPE: i8
|
||||
FUNCTION_PROTOTYPE: i16
|
||||
FUNCTION_PROTOTYPE: i32
|
||||
FUNCTION_PROTOTYPE: i64
|
||||
FUNCTION_PROTOTYPE: u8
|
||||
FUNCTION_PROTOTYPE: u16
|
||||
FUNCTION_PROTOTYPE: u32
|
||||
FUNCTION_PROTOTYPE: u64
|
||||
FUNCTION_PROTOTYPE: bool
|
||||
FUNCTION_PROTOTYPE: f32
|
||||
FUNCTION_PROTOTYPE: f64
|
||||
FUNCTION_PROTOTYPE: isize
|
||||
FUNCTION_PROTOTYPE: usize
|
||||
GLOBAL: HEAP_BASE
|
||||
CLASS_PROTOTYPE: class-extends/A
|
||||
CLASS_PROTOTYPE: class-extends/B
|
||||
FUNCTION_PROTOTYPE: class-extends/test
|
||||
[program.exports]
|
||||
FUNCTION_PROTOTYPE: class-extends/test
|
||||
;)
|
Loading…
x
Reference in New Issue
Block a user