Basic initial inheritance

This commit is contained in:
dcodeIO 2018-01-04 01:36:26 +01:00
parent ae99adefce
commit 50dea3b1df
7 changed files with 199 additions and 22 deletions

View File

@ -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();

View File

@ -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}'.";

View File

@ -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,

View File

@ -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()) {

View 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)
)
)
)

View 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;
}

View 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
;)