public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_NullPropagatingOpInExpressionTree, node); } return(base.VisitConditionalAccess(node)); }
// IL gen can generate more compact code for certain conditional accesses // by utilizing stack dup/pop instructions internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used) { Debug.Assert(!_inExpressionLambda); var loweredReceiver = this.VisitExpression(node.Receiver); var receiverType = loweredReceiver.Type; // Check trivial case if (loweredReceiver.IsDefaultValue() && receiverType.IsReferenceType) { return(_factory.Default(node.Type)); } ConditionalAccessLoweringKind loweringKind; // dynamic receivers are not directly supported in codegen and need to be lowered to a ternary var lowerToTernary = node.AccessExpression.Type.IsDynamic(); if (!lowerToTernary) { // trivial cases are directly supported in IL gen loweringKind = ConditionalAccessLoweringKind.LoweredConditionalAccess; } else if (CanChangeValueBetweenReads(loweredReceiver)) { // NOTE: dynamic operations historically do not propagate mutations // to the receiver if that happens to be a value type // so we can capture receiver by value in dynamic case regardless of // the type of receiver // Nullable receivers are immutable so should be captured by value as well. loweringKind = ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal; } else { loweringKind = ConditionalAccessLoweringKind.Ternary; } var previousConditionalAccessTarget = _currentConditionalAccessTarget; var currentConditionalAccessID = ++_currentConditionalAccessID; LocalSymbol temp = null; switch (loweringKind) { case ConditionalAccessLoweringKind.LoweredConditionalAccess: _currentConditionalAccessTarget = new BoundConditionalReceiver( loweredReceiver.Syntax, currentConditionalAccessID, receiverType); break; case ConditionalAccessLoweringKind.Ternary: _currentConditionalAccessTarget = loweredReceiver; break; case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal: temp = _factory.SynthesizedLocal(receiverType); _currentConditionalAccessTarget = _factory.Local(temp); break; default: throw ExceptionUtilities.UnexpectedValue(loweringKind); } BoundExpression loweredAccessExpression; if (used) { loweredAccessExpression = this.VisitExpression(node.AccessExpression); } else { loweredAccessExpression = this.VisitUnusedExpression(node.AccessExpression); if (loweredAccessExpression == null) { return(null); } } Debug.Assert(loweredAccessExpression != null); _currentConditionalAccessTarget = previousConditionalAccessTarget; TypeSymbol type = this.VisitType(node.Type); TypeSymbol nodeType = node.Type; TypeSymbol accessExpressionType = loweredAccessExpression.Type; if (accessExpressionType.SpecialType == SpecialType.System_Void) { type = nodeType = accessExpressionType; } if (!TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) && nodeType.IsNullableType()) { Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType.GetNullableUnderlyingType(), TypeCompareKind.ConsiderEverything2)); loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression); } else { Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) || (nodeType.SpecialType == SpecialType.System_Void && !used)); } BoundExpression result; var objectType = _compilation.GetSpecialType(SpecialType.System_Object); switch (loweringKind) { case ConditionalAccessLoweringKind.LoweredConditionalAccess: result = new BoundLoweredConditionalAccess( node.Syntax, loweredReceiver, receiverType.IsNullableType() ? UnsafeGetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.System_Nullable_T_get_HasValue) : null, loweredAccessExpression, null, currentConditionalAccessID, type); break; case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal: // capture the receiver into a temp loweredReceiver = _factory.MakeSequence( _factory.AssignmentExpression(_factory.Local(temp), loweredReceiver), _factory.Local(temp)); goto case ConditionalAccessLoweringKind.Ternary; case ConditionalAccessLoweringKind.Ternary: { // (object)r != null ? access : default(T) var condition = _factory.ObjectNotEqual( _factory.Convert(objectType, loweredReceiver), _factory.Null(objectType)); var consequence = loweredAccessExpression; result = RewriteConditionalOperator(node.Syntax, condition, consequence, _factory.Default(nodeType), null, nodeType, isRef: false); if (temp != null) { result = _factory.MakeSequence(temp, result); } } break; default: throw ExceptionUtilities.UnexpectedValue(loweringKind); } return(result); }
public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) { return(RewriteConditionalAccess(node, used: true)); }
private void EmitConditionalAccessExpression(BoundConditionalAccess expression, bool used) { var receiver = expression.Receiver; if (receiver.IsDefaultValue()) { EmitDefaultValue(expression.Type, used, expression.Syntax); return; } EmitExpression(receiver, used: true); var receiverConstant = receiver.ConstantValue; if (receiverConstant != null) { // const but not default EmitExpression(expression.AccessExpression, used); return; } builder.EmitOpCode(ILOpCode.Dup); if (!receiver.Type.IsVerifierReference()) { EmitBox(receiver.Type, receiver.Syntax); } object whenNotNull = new object(); object done = new object(); builder.EmitBranch(ILOpCode.Brtrue, whenNotNull); builder.EmitOpCode(ILOpCode.Pop); EmitDefaultValue(expression.Type, used, expression.Syntax); builder.EmitBranch(ILOpCode.Br, done); if(!used) { // when unused, notNull branch just pops the reciever off the stack // but in whenNotNull branch we still have it builder.AdjustStack(+1); } builder.MarkLabel(whenNotNull); EmitExpression(expression.AccessExpression, used); builder.MarkLabel(done); }
public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) { var origStack = _evalStack; BoundExpression receiver = VisitCallReceiver(node.Receiver); var cookie = GetStackStateCookie(); // implicit branch here // right is evaluated with original stack // (this is not entirely true, codegen will keep receiver on the stack, but that is irrelevant here) _evalStack = origStack; BoundExpression access = (BoundExpression)this.Visit(node.AccessExpression); EnsureStackState(cookie); // implicit label here return node.Update(receiver, access, node.Type); }
private void EmitConditionalAccessExpression(BoundConditionalAccess expression, bool used) { var receiver = expression.Receiver; if (receiver.IsDefaultValue()) { EmitDefaultValue(expression.Type, used, expression.Syntax); return; } var receiverType = receiver.Type; LocalDefinition receiverTemp = null; Debug.Assert(!receiverType.IsValueType, "conditional receiver cannot be a struct"); var receiverConstant = receiver.ConstantValue; if (receiverConstant != null) { // const but not default receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType); EmitExpression(expression.AccessExpression, used); if (receiverTemp != null) { FreeTemp(receiverTemp); } return; } // labels object whenNotNullLabel = new object(); object doneLabel = new object(); LocalDefinition temp = null; // we need a copy if we deal with nonlocal value (to capture the value) // or if we have a ref-constrained T (to do box just once) var nullCheckOnCopy = LocalRewriter.NeedsTemp(receiver, localsMayBeAssigned: false) || (receiverType.IsReferenceType && receiverType.TypeKind == TypeKind.TypeParameter) ; if (nullCheckOnCopy) { EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType); if (!receiverType.IsReferenceType) { // unconstrained case needs to handle case where T is actually a struct. // such values are never nulls // we will emit a check for such case, but the check is realy a JIT-time // constant since JIT will know if T is a struct or not. // if ((object)default(T) != null) // { // goto whenNotNull // } // else // { // temp = receiverRef // receiverRef = ref temp // } EmitDefaultValue(receiverType, true, receiver.Syntax); EmitBox(receiverType, receiver.Syntax); builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel); EmitLoadIndirect(receiverType, receiver.Syntax); temp = AllocateTemp(receiverType, receiver.Syntax); builder.EmitLocalStore(temp); builder.EmitLocalAddress(temp); builder.EmitLocalLoad(temp); EmitBox(receiver.Type, receiver.Syntax); // here we have loaded a ref to a temp and its boxed value { &T, O } } else { builder.EmitOpCode(ILOpCode.Dup); // here we have loaded two copies of a reference { O, O } } } else { EmitExpression(receiver, true); if (!receiverType.IsReferenceType) { EmitBox(receiverType, receiver.Syntax); } // here we have loaded just { O } // we have the most trivial case where we can just reload O when needed } builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel); if (nullCheckOnCopy) { builder.EmitOpCode(ILOpCode.Pop); } EmitDefaultValue(expression.Type, used, expression.Syntax); builder.EmitBranch(ILOpCode.Br, doneLabel); if (nullCheckOnCopy) { // notNull branch pops copy of receiver off the stack when nullCheckOnCopy // however on the isNull branch we still have the stack as it was and need // to adjust stack depth correspondingly. builder.AdjustStack(+1); } if (used) { // notNull branch pushes default on the stack when used // however on the isNull branch we still have the stack as it was and need // to adjust stack depth correspondingly. builder.AdjustStack(-1); } builder.MarkLabel(whenNotNullLabel); if (!nullCheckOnCopy) { receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType); Debug.Assert(receiverTemp == null); } EmitExpression(expression.AccessExpression, used); builder.MarkLabel(doneLabel); if (temp != null) { FreeTemp(temp); } if (receiverTemp != null) { FreeTemp(receiverTemp); } }
// IL gen can generate more compact code for certain conditional accesses // by utilizing stack dup/pop instructions internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used) { var loweredReceiver = this.VisitExpression(node.Receiver); var receiverType = loweredReceiver.Type; // Check trivial case if (loweredReceiver.IsDefaultValue() && receiverType.IsReferenceType) { return(_factory.Default(node.Type)); } ConditionalAccessLoweringKind loweringKind; // trivial cases are directly supported in IL gen loweringKind = ConditionalAccessLoweringKind.LoweredConditionalAccess; var previousConditionalAccessTarget = _currentConditionalAccessTarget; var currentConditionalAccessID = ++_currentConditionalAccessID; LocalSymbol temp = null; switch (loweringKind) { case ConditionalAccessLoweringKind.LoweredConditionalAccess: _currentConditionalAccessTarget = new BoundConditionalReceiver( loweredReceiver.Syntax, currentConditionalAccessID, receiverType); break; case ConditionalAccessLoweringKind.Ternary: _currentConditionalAccessTarget = loweredReceiver; break; case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal: temp = _factory.SynthesizedLocal(receiverType); _currentConditionalAccessTarget = _factory.Local(temp); break; default: throw ExceptionUtilities.UnexpectedValue(loweringKind); } BoundExpression loweredAccessExpression; if (used) { loweredAccessExpression = this.VisitExpression(node.AccessExpression); } else { loweredAccessExpression = this.VisitUnusedExpression(node.AccessExpression); if (loweredAccessExpression == null) { return(null); } } Debug.Assert(loweredAccessExpression != null); _currentConditionalAccessTarget = previousConditionalAccessTarget; TypeSymbol type = this.VisitType(node.Type); TypeSymbol nodeType = node.Type; TypeSymbol accessExpressionType = loweredAccessExpression.Type; if (accessExpressionType.SpecialType == SpecialType.System_Void) { type = nodeType = accessExpressionType; } if (!TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) && nodeType.IsNullableType()) { Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType.GetNullableUnderlyingType(), TypeCompareKind.ConsiderEverything2)); loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression); } else { Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) || (nodeType.SpecialType == SpecialType.System_Void && !used)); } BoundExpression result; var objectType = _compilation.GetSpecialType(SpecialType.System_Object); switch (loweringKind) { case ConditionalAccessLoweringKind.LoweredConditionalAccess: result = new BoundLoweredConditionalAccess( node.Syntax, loweredReceiver, receiverType.IsNullableType() ? UnsafeGetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.core_Option_T_get_has_value) : null, loweredAccessExpression, null, currentConditionalAccessID, type); break; case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal: // capture the receiver into a temp loweredReceiver = _factory.MakeSequence( _factory.AssignmentExpression(_factory.Local(temp), loweredReceiver), _factory.Local(temp)); goto case ConditionalAccessLoweringKind.Ternary; case ConditionalAccessLoweringKind.Ternary: { // (object)r != null ? access : default(T) var condition = _factory.ObjectNotEqual( _factory.Convert(objectType, loweredReceiver), _factory.Null(objectType)); var consequence = loweredAccessExpression; result = RewriteConditionalOperator(node.Syntax, condition, consequence, _factory.Default(nodeType), null, nodeType, isRef: false); if (temp != null) { result = _factory.MakeSequence(temp, result); } } break; default: throw ExceptionUtilities.UnexpectedValue(loweringKind); } return(result); }