internal void Parse(BoundLoweredConditionalAccess boundLoweredConditionalAccess) { base.Parse(boundLoweredConditionalAccess); this.Type = boundLoweredConditionalAccess.Receiver.Type; this.Receiver = Deserialize(boundLoweredConditionalAccess.Receiver) as Expression; this.WhenNotNull = Deserialize(boundLoweredConditionalAccess.WhenNotNull) as Expression; }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull); BoundExpression whenNullOpt = (BoundExpression)this.Visit(node.WhenNullOpt); TypeSymbol type = this.VisitType(node.Type); return node.Update(receiver, VisitMethodSymbol(node.HasValueMethodOpt), whenNotNull, whenNullOpt, node.Id, type); }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { var receiverRefKind = ReceiverSpillRefKind(node.Receiver); BoundSpillSequenceBuilder receiverBuilder = null; var receiver = VisitExpression(ref receiverBuilder, node.Receiver); BoundSpillSequenceBuilder whenNotNullBuilder = null; var whenNotNull = VisitExpression(ref whenNotNullBuilder, node.WhenNotNull); BoundSpillSequenceBuilder whenNullBuilder = null; var whenNullOpt = VisitExpression(ref whenNullBuilder, node.WhenNullOpt); if (whenNotNullBuilder == null && whenNullBuilder == null) { return(UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.Type))); } if (receiverBuilder == null) { receiverBuilder = new BoundSpillSequenceBuilder(); } if (whenNotNullBuilder == null) { whenNotNullBuilder = new BoundSpillSequenceBuilder(); } if (whenNullBuilder == null) { whenNullBuilder = new BoundSpillSequenceBuilder(); } BoundExpression condition; if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None) { // spill to a clone receiver = Spill(receiverBuilder, receiver, RefKind.None); var hasValueOpt = node.HasValueMethodOpt; if (hasValueOpt == null) { condition = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), receiver), _F.Null(_F.SpecialType(SpecialType.System_Object))); } else { condition = _F.Call(receiver, hasValueOpt); } } else { Debug.Assert(node.HasValueMethodOpt == null); receiver = Spill(receiverBuilder, receiver, RefKind.Ref); var clone = _F.SynthesizedLocal(receiver.Type, _F.Syntax, refKind: RefKind.None, kind: SynthesizedLocalKind.Spill); receiverBuilder.AddLocal(clone); // (object)default(T) != null var isNotClass = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Default(receiver.Type)), _F.Null(_F.SpecialType(SpecialType.System_Object))); // isNotCalss || {clone = receiver; (object)clone != null} condition = _F.LogicalOr( isNotClass, _F.MakeSequence( _F.AssignmentExpression(_F.Local(clone), receiver), _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Local(clone)), _F.Null(_F.SpecialType(SpecialType.System_Object)))) ); receiver = _F.ComplexConditionalReceiver(receiver, _F.Local(clone)); } if (node.Type.SpecialType == SpecialType.System_Void) { var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.ExpressionStatement(whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); Debug.Assert(whenNullOpt == null || !LocalRewriter.ReadIsSideeffecting(whenNullOpt)); receiverBuilder.AddStatement(_F.If(condition, whenNotNullStatement)); return(receiverBuilder.Update(_F.Default(node.Type))); } else { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.Assignment(_F.Local(tmp), whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); whenNullOpt = whenNullOpt ?? _F.Default(node.Type); receiverBuilder.AddLocal(tmp); receiverBuilder.AddStatement( _F.If(condition, whenNotNullStatement, UpdateStatement(whenNullBuilder, _F.Assignment(_F.Local(tmp), whenNullOpt)))); return(receiverBuilder.Update(_F.Local(tmp))); } }
private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAccess 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 || (receiverType.IsNullableType() && expression.HasValueMethodOpt != null), "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.WhenNotNull, used); if (receiverTemp != null) { FreeTemp(receiverTemp); } return; } // labels object whenNotNullLabel = new object(); object doneLabel = new object(); LocalDefinition cloneTemp = 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) // or if we deal with stack local (reads are destructive) var nullCheckOnCopy = LocalRewriter.CanChangeValueBetweenReads(receiver, localsMayBeAssignedOrCaptured: false) || (receiverType.IsReferenceType && receiverType.TypeKind == TypeKind.TypeParameter) || (receiver.Kind == BoundKind.Local && IsStackLocal(((BoundLocal)receiver).LocalSymbol)); var unconstrainedReceiver = !receiverType.IsReferenceType && !receiverType.IsValueType; // ===== RECEIVER if (nullCheckOnCopy) { receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: unconstrainedReceiver); if (unconstrainedReceiver) { // 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 really 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); cloneTemp = AllocateTemp(receiverType, receiver.Syntax); _builder.EmitLocalStore(cloneTemp); _builder.EmitLocalAddress(cloneTemp); _builder.EmitLocalLoad(cloneTemp); 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 } or {&nub, &nub} } } else { receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: false); // here we have loaded just { O } or {&nub} // we have the most trivial case where we can just reload receiver when needed again } // ===== CONDITION var hasValueOpt = expression.HasValueMethodOpt; if (hasValueOpt != null) { Debug.Assert(receiver.Type.IsNullableType()); _builder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0); EmitSymbolToken(hasValueOpt, expression.Syntax, null); } _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel); // no longer need the temp if we are not holding a copy if (receiverTemp != null && !nullCheckOnCopy) { FreeTemp(receiverTemp); receiverTemp = null; } // ===== WHEN NULL if (nullCheckOnCopy) { _builder.EmitOpCode(ILOpCode.Pop); } var whenNull = expression.WhenNullOpt; if (whenNull == null) { EmitDefaultValue(expression.Type, used, expression.Syntax); } else { EmitExpression(whenNull, used); } _builder.EmitBranch(ILOpCode.Br, doneLabel); // ===== WHEN NOT NULL 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) { Debug.Assert(receiverTemp == null); receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: unconstrainedReceiver); Debug.Assert(receiverTemp == null); } EmitExpression(expression.WhenNotNull, used); // ===== DONE _builder.MarkLabel(doneLabel); if (cloneTemp != null) { FreeTemp(cloneTemp); } if (receiverTemp != null) { FreeTemp(receiverTemp); } }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { var origStack = StackDepth(); 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) SetStackDepth(origStack); BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull); EnsureStackState(cookie); // implicit label here var whenNull = node.WhenNullOpt; if (whenNull != null) { SetStackDepth(origStack); // whenNull is evaluated with original stack whenNull = (BoundExpression)this.Visit(whenNull); EnsureStackState(cookie); // implicit label here } else { // compensate for the whenNull that we are not visiting. _counter += 1; } return node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNull, node.Id, node.Type); }
// 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 VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { throw ExceptionUtilities.Unreachable; }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull); BoundExpression whenNullOpt = (BoundExpression)this.Visit(node.WhenNullOpt); TypeSymbol type = this.VisitType(node.Type); return node.Update(receiver, VisitMethodSymbol(node.HasValueMethodOpt), whenNotNull, whenNullOpt, node.Id, type); }
// IL gen can generate more compact code for certain conditional accesses // by utilizing stack dup/pop instructions internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used, BoundExpression rewrittenWhenNull = null) { Debug.Assert(!_inExpressionLambda); var loweredReceiver = this.VisitExpression(node.Receiver); var receiverType = loweredReceiver.Type; // Check trivial case if (loweredReceiver.IsDefaultValue()) { return rewrittenWhenNull ?? _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 hapens 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 previousConditionalAccesTarget = _currentConditionalAccessTarget; var currentConditionalAccessID = ++this._currentConditionalAccessID; LocalSymbol temp = null; BoundExpression unconditionalAccess = 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 = used ? this.VisitExpression(node.AccessExpression) : this.VisitUnusedExpression(node.AccessExpression); _currentConditionalAccessTarget = previousConditionalAccesTarget; TypeSymbol type = this.VisitType(node.Type); TypeSymbol nodeType = node.Type; TypeSymbol accessExpressionType = loweredAccessExpression.Type; if (accessExpressionType.SpecialType == SpecialType.System_Void) { type = nodeType = accessExpressionType; } if (accessExpressionType != nodeType && nodeType.IsNullableType()) { Debug.Assert(accessExpressionType == nodeType.GetNullableUnderlyingType()); loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression); if (unconditionalAccess != null) { unconditionalAccess = _factory.New((NamedTypeSymbol)nodeType, unconditionalAccess); } } else { Debug.Assert(accessExpressionType == nodeType || (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() ? GetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.System_Nullable_T_get_HasValue): null, loweredAccessExpression, rewrittenWhenNull, currentConditionalAccessID, type); break; case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal: // capture the receiver into a temp loweredReceiver = _factory.Sequence( _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, rewrittenWhenNull ?? _factory.Default(nodeType), null, nodeType); if (temp != null) { result = _factory.Sequence(temp, result); } } break; default: throw ExceptionUtilities.Unreachable; } return result; }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = &v){ ... } == becomes ===> /// /// pinned ref int pinnedTemp = ref v; // pinning managed ref /// int* ptr = (int*)&pinnedTemp; // unsafe cast to unmanaged ptr /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementGetPinnable( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type.TypeSymbol; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); var initializerType = initializerExpr.Type; var initializerSyntax = initializerExpr.Syntax; var getPinnableMethod = fixedInitializer.GetPinnableOpt; // intervening parens may have been skipped by the binder; find the declarator VariableDeclarationSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclarationSyntax>(); Debug.Assert(declarator != null); // pinned ref int pinnedTemp pinnedTemp = factory.SynthesizedLocal( getPinnableMethod.ReturnType.TypeSymbol, syntax: declarator, isPinned: true, //NOTE: different from the array and string cases // RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways) refKind: RefKind.RefReadOnly, kind: SynthesizedLocalKind.FixedReference); BoundExpression callReceiver; int currentConditionalAccessID = 0; bool needNullCheck = !initializerType.IsValueType; if (needNullCheck) { currentConditionalAccessID = _currentConditionalAccessID++; callReceiver = new BoundConditionalReceiver( initializerSyntax, currentConditionalAccessID, initializerType); } else { callReceiver = initializerExpr; } // .GetPinnable() var getPinnableCall = getPinnableMethod.IsStatic ? factory.Call(null, getPinnableMethod, callReceiver) : factory.Call(callReceiver, getPinnableMethod); // temp =ref .GetPinnable() var tempAssignment = factory.AssignmentExpression( factory.Local(pinnedTemp), getPinnableCall, isRef: true); // &pinnedTemp var addr = new BoundAddressOfOperator( factory.Syntax, factory.Local(pinnedTemp), type: fixedInitializer.ElementPointerType); // (int*)&pinnedTemp var pointerValue = factory.Convert( localType, addr, fixedInitializer.ElementPointerTypeConversion); // {pinnedTemp =ref .GetPinnable(), (int*)&pinnedTemp} BoundExpression pinAndGetPtr = factory.Sequence( locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), result: pointerValue); if (needNullCheck) { // initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; pinAndGetPtr = new BoundLoweredConditionalAccess( initializerSyntax, initializerExpr, hasValueMethodOpt: null, whenNotNull: pinAndGetPtr, whenNullOpt: null, // just return default(T*) currentConditionalAccessID, localType); } // ptr = initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), pinAndGetPtr)); return(localInit); }
// 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); }