public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { BoundSpillSequenceBuilder builder = null; var right = VisitExpression(ref builder, node.RightOperand); BoundExpression left; if (builder == null) { left = VisitExpression(ref builder, node.LeftOperand); } else { var leftBuilder = new BoundSpillSequenceBuilder(); left = VisitExpression(ref leftBuilder, node.LeftOperand); left = Spill(leftBuilder, left); var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); leftBuilder.AddLocal(tmp); leftBuilder.AddStatement(_F.Assignment(_F.Local(tmp), left)); leftBuilder.AddStatement(_F.If( _F.ObjectEqual(_F.Local(tmp), _F.Null(left.Type)), UpdateStatement(builder, _F.Assignment(_F.Local(tmp), right)))); return(UpdateExpression(leftBuilder, _F.Local(tmp))); } return(UpdateExpression(builder, node.Update(left, right, node.LeftConversion, node.OperatorResultKind, node.Type))); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { BoundSpillSequenceBuilder builder = null; var right = VisitExpression(ref builder, node.RightOperand); BoundExpression left; if (builder == null) { left = VisitExpression(ref builder, node.LeftOperand); } else { var leftBuilder = new BoundSpillSequenceBuilder(); left = VisitExpression(ref leftBuilder, node.LeftOperand); left = Spill(leftBuilder, left); leftBuilder.AddStatement(_F.If( _F.ObjectEqual(left, _F.Null(left.Type)), UpdateStatement(builder, _F.Assignment(left, right), substituteTemps: false))); return(UpdateExpression(leftBuilder, left)); } return(UpdateExpression(builder, node.Update(left, right, node.LeftConversion, node.Type))); }
internal void Parse(BoundNullCoalescingOperator boundNullCoalescingOperator) { base.Parse(boundNullCoalescingOperator); this.LeftOperand = Deserialize(boundNullCoalescingOperator.LeftOperand) as Expression; this.RightOperand = Deserialize(boundNullCoalescingOperator.RightOperand) as Expression; this.ConversionKind = boundNullCoalescingOperator.LeftConversion.Kind; }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.RightOperand); BoundExpression left; if (ss == null) { left = VisitExpression(ref ss, node.LeftOperand); } else { var ssLeft = new BoundSpillSequence2(); left = VisitExpression(ref ssLeft, node.LeftOperand); left = Spill(ssLeft, left); ssLeft.Add(F.If( F.ObjectEqual(left, F.Null(left.Type)), UpdateStatement(ss, F.Assignment(left, right)) )); return(UpdateExpression(ssLeft, left)); } return(UpdateExpression(ss, node.Update(left, right, node.LeftConversion, node.Type))); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { BoundExpression rewrittenLeft = (BoundExpression)Visit(node.LeftOperand); BoundExpression rewrittenRight = (BoundExpression)Visit(node.RightOperand); TypeSymbol rewrittenResultType = VisitType(node.Type); return(MakeNullCoalescingOperator(node.Syntax, rewrittenLeft, rewrittenRight, node.LeftConversion, node.OperatorResultKind, rewrittenResultType)); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { if (_inExpressionLambda && (node.LeftOperand.IsLiteralNull() || node.LeftOperand.IsLiteralDefault())) { Error(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, node.LeftOperand); } return(base.VisitNullCoalescingOperator(node)); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { if (_inExpressionLambda && node.LeftOperand.ConstantValue != null && node.LeftOperand.ConstantValue.IsNull) { Error(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, node.LeftOperand); } return(base.VisitNullCoalescingOperator(node)); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { // TestExpression ?? ElseExpression if (node.ConstantValue == null && !node.HasErrors) { var testExpression = node.TestExpression; ConstantValue testExpressionConstantValue = testExpression.ConstantValue; if (testExpressionConstantValue != null) { // testExpression is a known compile time constant if (testExpressionConstantValue == ConstantValue.Null) { // testExpression is always null return(Visit(node.ElseExpression)); } // testExpression is always non null constant return(Visit(node.LeftConversion ?? node.TestExpression)); } else { TypeSymbol exprType = node.Type; BoundExpression elseExpression = node.ElseExpression; BoundExpression leftConversion = node.LeftConversion; //it's a BoundConversion, but we're passing by ref // There are two ways that a conversion of the test expression can be represented: // 1) If it was cast in source, then node.TestExpression could be a BoundConversion, or // 2) If the compiler generated a conversion to reconcile the types, then node.LeftConversion could be non-null. if (leftConversion == null) { if (IsUpdateRequiredForExplicitConversion(exprType, ref testExpression, ref elseExpression)) { return(node.Update( testExpression, elseExpression, leftConversion: null, constantValueOpt: null, type: exprType)); } } else if (IsUpdateRequiredForExplicitConversion(exprType, ref leftConversion, ref elseExpression)) { return(node.Update( (BoundExpression)Visit(testExpression), elseExpression, (BoundConversion)leftConversion, constantValueOpt: null, type: exprType)); } } } return(base.VisitNullCoalescingOperator(node)); }
private BoundExpression VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { var left = Visit(node.LeftOperand); var right = Visit(node.RightOperand); if (node.LeftConversion.IsUserDefined) { TypeSymbol lambdaParamType = node.LeftOperand.Type.StrippedType(); return(ExprFactory("Coalesce", left, right, MakeConversionLambda(node.LeftConversion, lambdaParamType, node.Type))); } else { return(ExprFactory("Coalesce", left, right)); } }
private BoundNode FuseNodes(BoundConditionalAccess conditionalAccess, BoundNullCoalescingOperator node) { var type = node.RightOperand.Type; conditionalAccess = conditionalAccess.Update(conditionalAccess.Receiver, conditionalAccess.AccessExpression, type); var whenNull = (BoundExpression)Visit(node.RightOperand); if (whenNull.IsDefaultValue() && whenNull.Type.SpecialType != SpecialType.System_Decimal) { whenNull = null; } return(RewriteConditionalAccess(conditionalAccess, used: true, rewrittenWhenNull: whenNull)); }
public override object VisitNullCoalescingOperator(BoundNullCoalescingOperator node, object arg) { VisitExpression(node.Left); if (IsConstantNull(node.Left)) { VisitExpression(node.Right); } else { var savedState = this.state.Clone(); VisitExpression(node.Right); this.state = savedState; } return(null); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { // check for common pattern a?.b ?? c where b is a value type // in such cases the result of the expression is either b or c // we can skip nullable wrapping/unwrapping BoundConditionalAccess conditionalAccess; if (TryGetOptimizableNullableConditionalAccess(node.LeftOperand, out conditionalAccess)) { return(FuseNodes(conditionalAccess, node)); } BoundExpression rewrittenLeft = (BoundExpression)Visit(node.LeftOperand); BoundExpression rewrittenRight = (BoundExpression)Visit(node.RightOperand); TypeSymbol rewrittenResultType = VisitType(node.Type); return(MakeNullCoalescingOperator(node.Syntax, rewrittenLeft, rewrittenRight, node.LeftConversion, rewrittenResultType)); }
/// <summary> /// Emit code for a null-coalescing operator. /// </summary> /// <remarks> /// x ?? y becomes /// push x /// dup x /// if pop != null goto LEFT_NOT_NULL /// pop /// push y /// LEFT_NOT_NULL: /// </remarks> private void EmitNullCoalescingOperator(BoundNullCoalescingOperator expr, bool used) { Debug.Assert(expr.LeftConversion.IsIdentity, "coalesce with nontrivial left conversions are lowered into ternary."); Debug.Assert(expr.Type.IsReferenceType); EmitExpression(expr.LeftOperand, used: true); // See the notes about verification type merges in EmitConditionalOperator var mergeTypeOfLeftValue = StackMergeType(expr.LeftOperand); if (used) { if (IsVarianceCast(expr.Type, mergeTypeOfLeftValue)) { EmitStaticCast(expr.Type, expr.Syntax); mergeTypeOfLeftValue = expr.Type; } else if (expr.Type.IsInterfaceType() && expr.Type != mergeTypeOfLeftValue) { EmitStaticCast(expr.Type, expr.Syntax); } _builder.EmitOpCode(ILOpCode.Dup); } if (expr.Type.IsTypeParameter()) { EmitBox(expr.Type, expr.LeftOperand.Syntax); } object ifLeftNotNullLabel = new object(); _builder.EmitBranch(ILOpCode.Brtrue, ifLeftNotNullLabel); if (used) { _builder.EmitOpCode(ILOpCode.Pop); } EmitExpression(expr.RightOperand, used); if (used) { var mergeTypeOfRightValue = StackMergeType(expr.RightOperand); if (IsVarianceCast(expr.Type, mergeTypeOfRightValue)) { EmitStaticCast(expr.Type, expr.Syntax); mergeTypeOfRightValue = expr.Type; } } _builder.MarkLabel(ifLeftNotNullLabel); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { var origStack = StackDepth(); BoundExpression left = (BoundExpression)this.Visit(node.LeftOperand); var cookie = GetStackStateCookie(); // implicit branch here // right is evaluated with original stack // (this is not entirely true, codegen may keep left on the stack as an ephemeral temp, but that is irrelevant here) SetStackDepth(origStack); BoundExpression right = (BoundExpression)this.Visit(node.RightOperand); EnsureStackState(cookie); // implicit label here return node.Update(left, right, node.LeftConversion, node.Type); }
public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { // TestExpression ?? ElseExpression if (node.ConstantValue == null && !node.HasErrors) { var testExpression = node.TestExpression; ConstantValue testExpressionConstantValue = testExpression.ConstantValue; if (testExpressionConstantValue != null) { // testExpression is a known compile time constant if (testExpressionConstantValue == ConstantValue.Null) { // testExpression is always null return Visit(node.ElseExpression); } // testExpression is always non null constant return Visit(node.LeftConversion ?? node.TestExpression); } else { TypeSymbol exprType = node.Type; BoundExpression elseExpression = node.ElseExpression; BoundExpression leftConversion = node.LeftConversion; //it's a BoundConversion, but we're passing by ref // There are two ways that a conversion of the test expression can be represented: // 1) If it was cast in source, then node.TestExpression could be a BoundConversion, or // 2) If the compiler generated a conversion to reconcile the types, then node.LeftConversion could be non-null. if (leftConversion == null) { if (IsUpdateRequiredForExplicitConversion(exprType, ref testExpression, ref elseExpression)) { return node.Update( testExpression, elseExpression, leftConversion: null, constantValueOpt: null, type: exprType); } } else if (IsUpdateRequiredForExplicitConversion(exprType, ref leftConversion, ref elseExpression)) { return node.Update( (BoundExpression)Visit(testExpression), elseExpression, (BoundConversion)leftConversion, constantValueOpt: null, type: exprType); } } } return base.VisitNullCoalescingOperator(node); }
private static BoundStatement RewriteFieldInitializer(BoundFieldEqualsValue fieldInit) { var syntax = fieldInit.Syntax; syntax = (syntax as EqualsValueClauseSyntax)?.Value ?? syntax; //we want the attached sequence point to indicate the value node var boundReceiver = fieldInit.Field.IsStatic ? null : new BoundThisReference(syntax, fieldInit.Field.ContainingType); #if XSHARP var initValue = fieldInit.Value; // a generated initial value for VO NULL_STRING initialization // should not overwrite a value set in a child class // not that we recommend that <g> bool wasGenerated = fieldInit.WasCompilerGenerated; if (!wasGenerated) { var xNode = initValue.Syntax as LiteralExpressionSyntax; if (xNode != null && xNode.XGenerated) { wasGenerated = true; } } if (wasGenerated && fieldInit.Field.Type.IsStringType() && boundReceiver != null && fieldInit.Field.DeclaringCompilation.Options.HasOption(CompilerOption.NullStrings, boundReceiver.Syntax)) { var fldaccess = new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null) { WasCompilerGenerated = true }; initValue = new BoundNullCoalescingOperator(syntax, fldaccess, initValue, Conversion.Identity, fieldInit.Field.Type) { WasCompilerGenerated = true }; } BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null), initValue, fieldInit.Field.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = fieldInit.WasCompilerGenerated }; #else BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null), fieldInit.Value, fieldInit.Field.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = !fieldInit.Locals.IsEmpty || fieldInit.WasCompilerGenerated }; #endif if (!fieldInit.Locals.IsEmpty) { boundStatement = new BoundBlock(syntax, fieldInit.Locals, ImmutableArray.Create(boundStatement)) { WasCompilerGenerated = fieldInit.WasCompilerGenerated }; } Debug.Assert(LocalRewriter.IsFieldOrPropertyInitializer(boundStatement)); return(boundStatement); }