diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index f8e3ac2e18..3db97c0294 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -164,25 +164,52 @@ public void ProcessStatement(DMASTProcStatement statement) { } } - public void ProcessStatementExpression(DMASTProcStatementExpression statement) { - _exprBuilder.Emit(statement.Expression, out var expr); - var checkedExpression = statement.Expression.GetUnwrapped(); - + /// + /// Returns true if the expression has DMASTCallableSelf as the leftmost innermost expression. + /// + /// + /// + public void HandleCallableSelfLeft(DMASTExpression expr, ref DMComplexValueType currentReturnType, DMExpression realExpr, out bool isTemporary, bool checkConditions = false) { + isTemporary = false; + var checkedExpression = expr.GetUnwrapped(); switch (checkedExpression) { + case DMASTNot: + DMASTUnary astUnary = (DMASTUnary)checkedExpression; + UnaryOp unaryExpr = (UnaryOp)realExpr; + HandleCallableSelfLeft(astUnary.Value.GetUnwrapped(), ref currentReturnType, unaryExpr.Expr, out var _); + break; + case DMASTEqual astEqual: // a special case: we don't set it but we know lhs = rhs inside this block anyway + if (!checkConditions) break; + if (astEqual.LHS.GetUnwrapped() is DMASTCallableSelf) { + isTemporary = true; // signal to the caller that this should be reset once we leave the current scope + if (realExpr is IsNull) { + // hate this, remove when it uses a bytecode op instead + currentReturnType = DMValueType.Null; + break; + } + Equal equalExpr = (Equal)realExpr; + currentReturnType = equalExpr.RHS.ValType; + } + break; case DMASTAssign astAssignment: - if (astAssignment.LHS is DMASTCallableSelf) - CurrentReturnType = expr.ValType; + Assignment assignExpr = (Assignment)realExpr; + if (astAssignment.LHS.GetUnwrapped() is DMASTCallableSelf) + currentReturnType = assignExpr.RHS.ValType; break; - case DMASTAppend: case DMASTLogicalOrAssign: DMASTBinary astBinary = (DMASTBinary)checkedExpression; - if (astBinary.LHS is not DMASTCallableSelf) + if (astBinary.LHS.GetUnwrapped() is not DMASTCallableSelf) break; - if (CurrentReturnType.Type != DMValueType.Null) + if (currentReturnType.Type != DMValueType.Null) break; - CurrentReturnType = DMComplexValueType.MergeComplexValueTypes(compiler, CurrentReturnType, expr.ValType); + currentReturnType = DMComplexValueType.MergeComplexValueTypes(compiler, currentReturnType, realExpr.ValType); break; + } } + + public void ProcessStatementExpression(DMASTProcStatementExpression statement) { + _exprBuilder.Emit(statement.Expression, out var expr); + HandleCallableSelfLeft(statement.Expression, ref CurrentReturnType, expr, out var _); proc.Pop(); } @@ -474,24 +501,9 @@ public void ProcessStatementReturn(DMASTProcStatementReturn statement) { public void ProcessStatementIf(DMASTProcStatementIf statement) { _exprBuilder.Emit(statement.Condition, out var expr); - var checkedCondition = statement.Condition.GetUnwrapped(); - - switch (checkedCondition) { - case DMASTAssign astAssignment: - if (astAssignment.LHS is DMASTCallableSelf) - CurrentReturnType = expr.ValType; - break; - case DMASTAppend: - case DMASTLogicalOrAssign: - DMASTBinary astBinary = (DMASTBinary)checkedCondition; - if (astBinary.LHS is not DMASTCallableSelf) - break; - if (CurrentReturnType.Type != DMValueType.Null) - break; - CurrentReturnType = DMComplexValueType.MergeComplexValueTypes(compiler, CurrentReturnType, expr.ValType); - break; - } - var oldReturnType = CurrentReturnType; + var prevReturnType = CurrentReturnType; + HandleCallableSelfLeft(statement.Condition, ref CurrentReturnType, expr, out var isTemporary, checkConditions: true); + var condReturnType = CurrentReturnType; if (statement.ElseBody == null) { string endLabel = proc.NewLabelName(); @@ -500,7 +512,7 @@ public void ProcessStatementIf(DMASTProcStatementIf statement) { proc.StartScope(); ProcessBlockInner(statement.Body); proc.EndScope(); - CurrentReturnType = oldReturnType; + CurrentReturnType = condReturnType; proc.AddLabel(endLabel); } else { string elseLabel = proc.NewLabelName(); @@ -511,7 +523,9 @@ public void ProcessStatementIf(DMASTProcStatementIf statement) { proc.StartScope(); ProcessBlockInner(statement.Body); proc.EndScope(); - CurrentReturnType = oldReturnType; + CurrentReturnType = condReturnType; + if (isTemporary) + CurrentReturnType = prevReturnType; proc.Jump(endLabel); proc.AddLabel(elseLabel); @@ -522,6 +536,8 @@ public void ProcessStatementIf(DMASTProcStatementIf statement) { proc.EndScope(); proc.AddLabel(endLabel); } + if (isTemporary) + CurrentReturnType = prevReturnType; } public void ProcessStatementFor(DMASTProcStatementFor statementFor) { @@ -876,23 +892,8 @@ public void ProcessStatementSwitch(DMASTProcStatementSwitch statementSwitch) { DMASTProcBlockInner? defaultCaseBody = null; _exprBuilder.Emit(statementSwitch.Value, out var expr); - var checkedValue = statementSwitch.Value.GetUnwrapped(); - - switch (checkedValue) { - case DMASTAssign astAssignment: - if (astAssignment.LHS is DMASTCallableSelf) - CurrentReturnType = expr.ValType; - break; - case DMASTAppend: - case DMASTLogicalOrAssign: - DMASTBinary astBinary = (DMASTBinary)checkedValue; - if (astBinary.LHS is not DMASTCallableSelf) - break; - if (CurrentReturnType.Type != DMValueType.Null) - break; - CurrentReturnType = DMComplexValueType.MergeComplexValueTypes(compiler, CurrentReturnType, expr.ValType); - break; - } + HandleCallableSelfLeft(statementSwitch.Value, ref CurrentReturnType, expr, out var _); + // todo: some sort of return type inference based on cases for conditions switching on . foreach (DMASTProcStatementSwitch.SwitchCase switchCase in statementSwitch.Cases) { if (switchCase is DMASTProcStatementSwitch.SwitchCaseValues switchCaseValues) { string caseLabel = proc.NewLabelName(); diff --git a/DMCompiler/DM/Expressions/Binary.cs b/DMCompiler/DM/Expressions/Binary.cs index 78a5f92504..c519baf187 100644 --- a/DMCompiler/DM/Expressions/Binary.cs +++ b/DMCompiler/DM/Expressions/Binary.cs @@ -5,8 +5,8 @@ namespace DMCompiler.DM.Expressions; internal abstract class BinaryOp(Location location, DMExpression lhs, DMExpression rhs) : DMExpression(location) { - protected DMExpression LHS { get; } = lhs; - protected DMExpression RHS { get; } = rhs; + public DMExpression LHS { get; } = lhs; + public DMExpression RHS { get; } = rhs; public override DMComplexValueType ValType => LHS.ValType; public override bool PathIsFuzzy => true; diff --git a/DMCompiler/DM/Expressions/Unary.cs b/DMCompiler/DM/Expressions/Unary.cs index 889e23a25c..4e6a0704ee 100644 --- a/DMCompiler/DM/Expressions/Unary.cs +++ b/DMCompiler/DM/Expressions/Unary.cs @@ -4,7 +4,7 @@ namespace DMCompiler.DM.Expressions; internal abstract class UnaryOp(Location location, DMExpression expr) : DMExpression(location) { - protected DMExpression Expr { get; } = expr; + public DMExpression Expr { get; } = expr; } // -x