Skip to content

Commit d571c42

Browse files
committed
Initial fix for |= operator
1 parent 7e237cc commit d571c42

1 file changed

Lines changed: 206 additions & 1 deletion

File tree

src/compiler.ts

Lines changed: 206 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ export class Compiler extends DiagnosticEmitter {
476476
hasCustomFunctionExports: bool = false;
477477
/** Whether the module would use the exported runtime to lift/lower. */
478478
desiresExportRuntime: bool = false;
479+
/** Unique suffix for synthesized scoped locals used by compound assignments. */
480+
private compoundAssignmentTempId: i32 = 0;
479481

480482
/** Compiles a {@link Program} to a {@link Module} using the specified options. */
481483
static compile(program: Program): Module {
@@ -4473,7 +4475,121 @@ export class Compiler extends DiagnosticEmitter {
44734475
expr = this.makeAnd(leftExpr, rightExpr, commonType);
44744476
break;
44754477
}
4476-
case Token.Bar_Equals: compound = true;
4478+
case Token.Bar_Equals: {
4479+
compound = true;
4480+
let assignmentLeft = left;
4481+
let setupExprs: ExpressionRef[] | null = null;
4482+
if (this.needsCompoundAssignmentSideEffectCache(left)) {
4483+
let flow = this.currentFlow;
4484+
if (left.kind == NodeKind.PropertyAccess) {
4485+
let access = <PropertyAccessExpression>left;
4486+
let receiverExpression = access.expression;
4487+
if (this.expressionHasSideEffects(receiverExpression)) {
4488+
let receiverExpr = this.compileExpression(receiverExpression, Type.auto);
4489+
let receiverType = this.currentType;
4490+
let receiverTemp = flow.getTempLocal(receiverType);
4491+
let receiverName = this.makeCompoundAssignmentTempName(flow);
4492+
flow.addScopedAlias(receiverName, receiverType, receiverTemp.index);
4493+
flow.setLocalFlag(receiverTemp.index, LocalFlags.Initialized);
4494+
setupExprs = [ module.local_set(receiverTemp.index, receiverExpr, receiverType.isManaged) ];
4495+
assignmentLeft = Node.createPropertyAccessExpression(
4496+
Node.createIdentifierExpression(receiverName, receiverExpression.range),
4497+
access.property,
4498+
access.range
4499+
);
4500+
}
4501+
} else {
4502+
let access = <ElementAccessExpression>left;
4503+
let receiverExpression = access.expression;
4504+
let elementExpression = access.elementExpression;
4505+
let receiverName: string | null = null;
4506+
let elementName: string | null = null;
4507+
4508+
if (this.expressionHasSideEffects(receiverExpression)) {
4509+
let receiverExpr = this.compileExpression(receiverExpression, Type.auto);
4510+
let receiverType = this.currentType;
4511+
let receiverTemp = flow.getTempLocal(receiverType);
4512+
receiverName = this.makeCompoundAssignmentTempName(flow);
4513+
flow.addScopedAlias(receiverName, receiverType, receiverTemp.index);
4514+
flow.setLocalFlag(receiverTemp.index, LocalFlags.Initialized);
4515+
if (!setupExprs) setupExprs = [];
4516+
setupExprs.push(module.local_set(receiverTemp.index, receiverExpr, receiverType.isManaged));
4517+
}
4518+
4519+
if (this.expressionHasSideEffects(elementExpression)) {
4520+
let elementExpr = this.compileExpression(elementExpression, Type.auto);
4521+
let elementType = this.currentType;
4522+
let elementTemp = flow.getTempLocal(elementType);
4523+
elementName = this.makeCompoundAssignmentTempName(flow);
4524+
flow.addScopedAlias(elementName, elementType, elementTemp.index);
4525+
flow.setLocalFlag(elementTemp.index, LocalFlags.Initialized);
4526+
if (!setupExprs) setupExprs = [];
4527+
setupExprs.push(module.local_set(elementTemp.index, elementExpr, elementType.isManaged));
4528+
}
4529+
4530+
assignmentLeft = Node.createElementAccessExpression(
4531+
receiverName
4532+
? Node.createIdentifierExpression(receiverName, receiverExpression.range)
4533+
: receiverExpression,
4534+
elementName
4535+
? Node.createIdentifierExpression(elementName, elementExpression.range)
4536+
: elementExpression,
4537+
access.range
4538+
);
4539+
}
4540+
}
4541+
4542+
leftExpr = this.compileExpression(assignmentLeft, contextualType.intType);
4543+
leftType = this.currentType;
4544+
if (setupExprs) {
4545+
let wrappedExprs = setupExprs;
4546+
wrappedExprs.push(leftExpr);
4547+
leftExpr = module.block(null, wrappedExprs, leftType.toRef());
4548+
}
4549+
4550+
// check operator overload
4551+
let classReference = leftType.getClassOrWrapper(this.program);
4552+
if (classReference) {
4553+
let overload = classReference.lookupOverload(OperatorKind.BitwiseOr);
4554+
if (overload) {
4555+
expr = this.compileBinaryOverload(overload, assignmentLeft, leftExpr, leftType, right, expression);
4556+
break;
4557+
}
4558+
}
4559+
4560+
if (!leftType.isIntegerValue) {
4561+
this.error(
4562+
DiagnosticCode.The_0_operator_cannot_be_applied_to_type_1,
4563+
expression.range, "|", leftType.toString()
4564+
);
4565+
return module.unreachable();
4566+
}
4567+
rightExpr = this.compileExpression(right, leftType, Constraints.ConvImplicit);
4568+
rightType = commonType = this.currentType;
4569+
expr = this.makeOr(leftExpr, rightExpr, commonType);
4570+
4571+
let resolver = this.resolver;
4572+
let target = resolver.lookupExpression(assignmentLeft, this.currentFlow);
4573+
if (!target) return module.unreachable();
4574+
let targetType = resolver.getTypeOfElement(target);
4575+
if (!targetType) targetType = Type.void;
4576+
if (!this.currentType.isStrictlyAssignableTo(targetType)) {
4577+
this.error(
4578+
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
4579+
expression.range, this.currentType.toString(), targetType.toString()
4580+
);
4581+
return module.unreachable();
4582+
}
4583+
return this.makeAssignment(
4584+
target,
4585+
expr,
4586+
this.currentType,
4587+
right,
4588+
resolver.currentThisExpression,
4589+
resolver.currentElementExpression,
4590+
contextualType != Type.void
4591+
);
4592+
}
44774593
case Token.Bar: {
44784594
leftExpr = this.compileExpression(left, contextualType.intType);
44794595
leftType = this.currentType;
@@ -4790,6 +4906,95 @@ export class Compiler extends DiagnosticEmitter {
47904906
);
47914907
}
47924908

4909+
private makeCompoundAssignmentTempName(flow: Flow): string {
4910+
let name: string;
4911+
do {
4912+
let id = this.compoundAssignmentTempId++;
4913+
name = "__as_or_assign_tmp" + id.toString();
4914+
} while (flow.lookup(name));
4915+
return name;
4916+
}
4917+
4918+
private needsCompoundAssignmentSideEffectCache(target: Expression): bool {
4919+
if (target.kind == NodeKind.PropertyAccess) {
4920+
return this.expressionHasSideEffects((<PropertyAccessExpression>target).expression);
4921+
}
4922+
if (target.kind == NodeKind.ElementAccess) {
4923+
let access = <ElementAccessExpression>target;
4924+
return this.expressionHasSideEffects(access.expression)
4925+
|| this.expressionHasSideEffects(access.elementExpression);
4926+
}
4927+
return false;
4928+
}
4929+
4930+
private expressionHasSideEffects(expression: Expression): bool {
4931+
while (expression.kind == NodeKind.Parenthesized) {
4932+
expression = (<ParenthesizedExpression>expression).expression;
4933+
}
4934+
switch (expression.kind) {
4935+
case NodeKind.Call:
4936+
case NodeKind.New:
4937+
case NodeKind.UnaryPostfix:
4938+
case NodeKind.Class:
4939+
case NodeKind.Function:
4940+
case NodeKind.Compiled:
4941+
return true;
4942+
case NodeKind.UnaryPrefix: {
4943+
let unary = <UnaryPrefixExpression>expression;
4944+
if (unary.operator == Token.Plus_Plus || unary.operator == Token.Minus_Minus) {
4945+
return true;
4946+
}
4947+
return this.expressionHasSideEffects(unary.operand);
4948+
}
4949+
case NodeKind.Binary: {
4950+
let binary = <BinaryExpression>expression;
4951+
switch (binary.operator) {
4952+
case Token.Equals:
4953+
case Token.Plus_Equals:
4954+
case Token.Minus_Equals:
4955+
case Token.Asterisk_Equals:
4956+
case Token.Asterisk_Asterisk_Equals:
4957+
case Token.Slash_Equals:
4958+
case Token.Percent_Equals:
4959+
case Token.LessThan_LessThan_Equals:
4960+
case Token.GreaterThan_GreaterThan_Equals:
4961+
case Token.GreaterThan_GreaterThan_GreaterThan_Equals:
4962+
case Token.Ampersand_Equals:
4963+
case Token.Bar_Equals:
4964+
case Token.Caret_Equals:
4965+
return true;
4966+
default:
4967+
return this.expressionHasSideEffects(binary.left)
4968+
|| this.expressionHasSideEffects(binary.right);
4969+
}
4970+
}
4971+
case NodeKind.Assertion:
4972+
return this.expressionHasSideEffects((<AssertionExpression>expression).expression);
4973+
case NodeKind.Comma: {
4974+
let expressions = (<CommaExpression>expression).expressions;
4975+
for (let i = 0, k = expressions.length; i < k; ++i) {
4976+
if (this.expressionHasSideEffects(unchecked(expressions[i]))) return true;
4977+
}
4978+
return false;
4979+
}
4980+
case NodeKind.ElementAccess: {
4981+
let access = <ElementAccessExpression>expression;
4982+
return this.expressionHasSideEffects(access.expression)
4983+
|| this.expressionHasSideEffects(access.elementExpression);
4984+
}
4985+
case NodeKind.PropertyAccess:
4986+
return this.expressionHasSideEffects((<PropertyAccessExpression>expression).expression);
4987+
case NodeKind.Ternary: {
4988+
let ternary = <TernaryExpression>expression;
4989+
return this.expressionHasSideEffects(ternary.condition)
4990+
|| this.expressionHasSideEffects(ternary.ifThen)
4991+
|| this.expressionHasSideEffects(ternary.ifElse);
4992+
}
4993+
default:
4994+
return false;
4995+
}
4996+
}
4997+
47934998
makeLt(leftExpr: ExpressionRef, rightExpr: ExpressionRef, type: Type): ExpressionRef {
47944999
// Cares about garbage bits and signedness
47955000
let module = this.module;

0 commit comments

Comments
 (0)