Binary expression operator overloads for +/==; Check allocation flow in ternary expressions; Cache empty array buffers; Sealed decorator for non-derivable internals

This commit is contained in:
dcodeIO
2018-03-23 12:45:29 +01:00
parent 9cc0fcd611
commit 4adb69f73a
18 changed files with 5074 additions and 267 deletions

View File

@ -2672,7 +2672,18 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createBinary(BinaryOp.EqI32, leftExpr, rightExpr);
break;
}
case TypeKind.USIZE: // TODO: check operator overload
case TypeKind.USIZE: { // check operator overload
if (this.currentType.is(TypeFlags.REFERENCE)) {
let classInstance = assert(this.currentType.classReference);
let operatorName = classInstance.prototype.fnEquals;
if (operatorName != null) {
expr = this.compileOperatorOverload(classInstance, operatorName, leftExpr, rightExpr);
assert(this.currentType == Type.bool);
break;
}
}
// fall-through
}
case TypeKind.ISIZE: {
expr = module.createBinary(
this.options.isWasm64
@ -2822,7 +2833,17 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createBinary(BinaryOp.AddI32, leftExpr, rightExpr);
break;
}
case TypeKind.USIZE: // TODO: check operator overload
case TypeKind.USIZE: { // check operator overload
if (this.currentType.is(TypeFlags.REFERENCE)) {
let classInstance = assert(this.currentType.classReference);
let operatorName = classInstance.prototype.fnConcat;
if (operatorName != null) {
expr = this.compileOperatorOverload(classInstance, operatorName, leftExpr, rightExpr);
break;
}
}
// fall-through
}
case TypeKind.ISIZE: {
expr = module.createBinary(
this.options.isWasm64
@ -3726,6 +3747,20 @@ export class Compiler extends DiagnosticEmitter {
: expr;
}
compileOperatorOverload(
classInstance: Class,
operatorName: string,
leftExpr: ExpressionRef,
rightExpr: ExpressionRef
): ExpressionRef {
var classPrototype = classInstance.prototype;
var operatorPrototype = assert(assert(classPrototype.members).get(operatorName));
assert(operatorPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
var operatorInstance = (<FunctionPrototype>operatorPrototype).resolve();
if (!operatorInstance) return this.module.createUnreachable();
return this.makeCallDirect(operatorInstance, [ leftExpr, rightExpr ]);
}
compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef {
var currentFunction = this.currentFunction;
var resolved = this.program.resolveExpression(expression, currentFunction); // reports
@ -4526,7 +4561,6 @@ export class Compiler extends DiagnosticEmitter {
assert(parent.kind == ElementKind.CLASS);
let thisType = (<Class>parent).type;
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
let nativeSizeType = this.options.nativeSizeType;
let flow = currentFunction.flow;
if (!flow.is(FlowFlags.ALLOCATES)) {
flow.set(FlowFlags.ALLOCATES);
@ -5152,8 +5186,36 @@ export class Compiler extends DiagnosticEmitter {
: this.compileExpression(ifElse, contextualType);
}
var ifThenExpr = this.compileExpression(ifThen, contextualType);
var ifElseExpr = this.compileExpression(ifElse, contextualType);
var currentFunction = this.currentFunction;
var ifThenExpr: ExpressionRef;
var ifElseExpr: ExpressionRef;
// if part of a constructor, keep track of memory allocations
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
let flow = currentFunction.flow;
flow = flow.enterBranchOrScope();
currentFunction.flow = flow;
ifThenExpr = this.compileExpression(ifThen, contextualType);
let ifThenAllocates = flow.is(FlowFlags.ALLOCATES);
flow = flow.leaveBranchOrScope();
currentFunction.flow = flow;
flow = flow.enterBranchOrScope();
currentFunction.flow = flow;
ifElseExpr = this.compileExpression(ifElse, contextualType);
let ifElseAllocates = flow.is(FlowFlags.ALLOCATES);
flow = flow.leaveBranchOrScope();
currentFunction.flow = flow;
if (ifThenAllocates && ifElseAllocates) flow.set(FlowFlags.ALLOCATES);
// otherwise simplify
} else {
ifThenExpr = this.compileExpression(ifThen, contextualType);
ifElseExpr = this.compileExpression(ifElse, contextualType);
}
return this.module.createIf(condExpr, ifThenExpr, ifElseExpr);
}

View File

@ -20,6 +20,7 @@ export enum DiagnosticCode {
Structs_cannot_implement_interfaces = 208,
Invalid_regular_expression_flags = 209,
Implementation_0_must_match_the_signature_1 = 210,
Class_0_is_sealed_and_cannot_be_extended = 211,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
@ -122,6 +123,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 208: return "Structs cannot implement interfaces.";
case 209: return "Invalid regular expression flags.";
case 210: return "Implementation '{0}' must match the signature '{1}'.";
case 211: return "Class '{0}' is sealed and cannot be extended.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";

View File

@ -12,6 +12,7 @@
"Structs cannot implement interfaces.": 208,
"Invalid regular expression flags.": 209,
"Implementation '{0}' must match the signature '{1}'.": 210,
"Class '{0}' is sealed and cannot be extended.": 211,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,

View File

@ -171,6 +171,10 @@ export class Parser extends DiagnosticEmitter {
flags |= CommonFlags.UNMANAGED;
continue;
}
if (text == "sealed") {
flags |= CommonFlags.SEALED;
continue;
}
}
if (!decorators) decorators = [];
decorators.push(decorator);

View File

@ -2041,34 +2041,36 @@ export enum CommonFlags {
BUILTIN = 1 << 14,
/** Is unmanaged. */
UNMANAGED = 1 << 15,
/** Is sealed. */
SEALED = 1 << 16,
// Extended modifiers usually derived from basic modifiers or internal decorators
/** Is ambient, that is either declared or nested in a declared element. */
AMBIENT = 1 << 16,
AMBIENT = 1 << 17,
/** Is generic. */
GENERIC = 1 << 17,
GENERIC = 1 << 18,
/** Is part of a generic context. */
GENERIC_CONTEXT = 1 << 18,
GENERIC_CONTEXT = 1 << 19,
/** Is an instance member. */
INSTANCE = 1 << 19,
INSTANCE = 1 << 20,
/** Is a constructor. */
CONSTRUCTOR = 1 << 20,
CONSTRUCTOR = 1 << 21,
/** Is an arrow function. */
ARROW = 1 << 21,
ARROW = 1 << 22,
/** Is a module export. */
MODULE_EXPORT = 1 << 22,
MODULE_EXPORT = 1 << 23,
/** Is a module import. */
MODULE_IMPORT = 1 << 23,
MODULE_IMPORT = 1 << 24,
// Compilation states
/** Is compiled. */
COMPILED = 1 << 24,
COMPILED = 1 << 25,
/** Has a constant value and is therefore inlined. */
INLINED = 1 << 25,
INLINED = 1 << 26,
/** Is scoped. */
SCOPED = 1 << 26
SCOPED = 1 << 27
}
/** Base class of all program elements. */
@ -2873,6 +2875,13 @@ export class ClassPrototype extends Element {
);
return null;
}
if (baseClass.is(CommonFlags.SEALED)) {
this.program.error(
DiagnosticCode.Class_0_is_sealed_and_cannot_be_extended,
declaration.extendsType.range, baseClass.internalName
);
return null;
}
if (baseClass.prototype.is(CommonFlags.UNMANAGED) != this.is(CommonFlags.UNMANAGED)) {
this.program.error(
DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa,