/// <summary> /// Applies the conversions. /// Adds any new locals to the temps and any new expressions to be evaluated to the stores. /// </summary> private void ApplyConversions(BoundDeconstructionAssignmentOperator node, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders) { int numConversions = node.ConversionSteps.Length; var conversionLocals = ArrayBuilder<BoundExpression>.GetInstance(); foreach (var conversionInfo in node.ConversionSteps) { // lower the conversions and assignments to locals var localSymbol = new SynthesizedLocal(_factory.CurrentMethod, conversionInfo.OutputPlaceholder.Type, SynthesizedLocalKind.LoweringTemp); var localBound = new BoundLocal(node.Syntax, localSymbol, null, conversionInfo.OutputPlaceholder.Type) { WasCompilerGenerated = true }; temps.Add(localSymbol); conversionLocals.Add(localBound); AddPlaceholderReplacement(conversionInfo.OutputPlaceholder, localBound); placeholders.Add(conversionInfo.OutputPlaceholder); var conversion = VisitExpression(conversionInfo.Assignment); stores.Add(conversion); } }
public static DecisionTree Create(BoundExpression expression, TypeSymbol type, Symbol enclosingSymbol) { Debug.Assert(expression.Type == type); LocalSymbol temp = null; if (expression.ConstantValue == null) { // Unless it is a constant, the decision tree acts on a copy of the input expression. // We create a temp to represent that copy. Lowering will assign into this temp. temp = new SynthesizedLocal(enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, expression.Syntax, false, RefKind.None); expression = new BoundLocal(expression.Syntax, temp, null, type); } if (expression.Type.CanBeAssignedNull()) { // We need the ByType decision tree to separate null from non-null values. // Note that, for the purpose of the decision tree (and subsumption), we // ignore the fact that the input may be a constant, and therefore always // or never null. return new ByType(expression, type, temp); } else { // If it is a (e.g. builtin) value type, we can switch on its (constant) values. // If it isn't a builtin, in practice we will only use the Default part of the // ByValue. return new ByValue(expression, type, temp); } }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> /// <param name="argument"></param> /// <param name="refKind"></param> /// <param name="containingMethod"></param> /// <returns></returns> public static Pair<BoundAssignmentOperator, BoundLocal> StoreToTemp(BoundExpression argument, RefKind refKind, MethodSymbol containingMethod) { var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, // TODO: the following temp local symbol should have its ContainingSymbol set. new TempLocalSymbol(type, refKind, containingMethod), null, type); var store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return Pair.Make(store, local); }
public override BoundNode VisitDeclarationExpression(BoundDeclarationExpression node) { var local = new BoundLocal(node.Syntax, node.LocalSymbol, null, node.LocalSymbol.Type); if (node.InitializerOpt == null) { return local; } return new BoundSequence(node.Syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create<BoundExpression>(new BoundAssignmentOperator( node.Syntax, new BoundLocal( local.Syntax, local.LocalSymbol, null, local.Type ), VisitExpression(node.InitializerOpt), local.Type)), local, local.Type); }
private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, DecisionMaker makeDecision) { if (byType.Default != null) { try { return AddByType(byType.Default, type, makeDecision); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } foreach (var kvp in byType.TypeAndDecision) { var MatchedType = kvp.Key; var Decision = kvp.Value; // See if matching Type matches this value switch (ExpressionOfTypeMatchesPatternType(type, MatchedType, ref _useSiteDiagnostics)) { case true: if (Decision.MatchIsComplete) { return null; } continue; case false: continue; case null: continue; } } var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None); var expression = new BoundLocal(Syntax, localSymbol, null, type); var result = makeDecision(expression, type); Debug.Assert(result.Temp == null); result.Temp = localSymbol; byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, result)); if (ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true) { byType.MatchIsComplete = true; } return result; }
public override BoundNode VisitLocal(BoundLocal node) { if (lambdaLevel != 0 && locals.ContainsKey(node.LocalSymbol)) { captured = true; } return null; }
protected override void VisitLvalue(BoundLocal node) { VisitLocal(node); }
/// <summary> /// Lower a foreach loop that will enumerate a collection using an enumerator. /// /// E e = ((C)(x)).GetEnumerator() /// try { /// while (e.MoveNext()) { /// V v = (V)(T)e.Current; /// // body /// } /// } /// finally { /// // clean up e /// } /// </summary> private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt; Debug.Assert(enumeratorInfo != null); BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType; TypeSymbol elementType = enumeratorInfo.ElementType; // E e LocalSymbol enumeratorVar = factory.SynthesizedLocal(enumeratorType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachEnumerator); // Reference to e. BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType); // ((C)(x)).GetEnumerator() or (x).GetEnumerator(); BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, enumeratorInfo.GetEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType); // E e = ((C)(x)).GetEnumerator(); BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue); AddForEachExpressionSequencePoint(forEachSyntax, ref enumeratorVarDecl); // V v LocalSymbol iterationVar = node.IterationVariable; //(V)(T)e.Current BoundExpression iterationVarAssignValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: MakeConversion( syntax: forEachSyntax, rewrittenOperand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.CurrentPropertyGetter), conversion: enumeratorInfo.CurrentConversion, rewrittenType: elementType, @checked: node.Checked), conversion: node.ElementConversion, rewrittenType: iterationVar.Type, @checked: node.Checked); // V v = (V)(T)e.Current; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarAssignValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* node.Body */ // } BoundStatement whileLoop = RewriteWhileStatement( syntax: forEachSyntax, rewrittenCondition: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.MoveNextMethod), conditionSequencePointSpan: forEachSyntax.InKeyword.Span, rewrittenBody: new BoundBlock(rewrittenBody.Syntax, statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody), locals: ImmutableArray.Create <LocalSymbol>(iterationVar)), breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: false); BoundStatement result; MethodSymbol disposeMethod; if (enumeratorInfo.NeedsDisposeMethod && TryGetSpecialTypeMember(forEachSyntax, SpecialMember.System_IDisposable__Dispose, out disposeMethod)) { Binder.ReportDiagnosticsIfObsolete(diagnostics, disposeMethod, forEachSyntax, hasBaseReceiver: false, containingMember: this.factory.CurrentMethod, containingType: this.factory.CurrentClass, location: enumeratorInfo.Location); BoundBlock finallyBlockOpt; var idisposableTypeSymbol = disposeMethod.ContainingType; var conversions = new TypeConversions(this.factory.CurrentMethod.ContainingAssembly.CorLibrary); HashSet <DiagnosticInfo> useSiteDiagnostics = null; var isImplicit = conversions.ClassifyImplicitConversion(enumeratorType, idisposableTypeSymbol, ref useSiteDiagnostics).IsImplicit; diagnostics.Add(forEachSyntax, useSiteDiagnostics); if (isImplicit) { Debug.Assert(enumeratorInfo.NeedsDisposeMethod); Conversion receiverConversion = enumeratorType.IsStructType() ? Conversion.Boxing : Conversion.ImplicitReference; // ((IDisposable)e).Dispose(); or e.Dispose(); BoundStatement disposeCall = new BoundExpressionStatement(forEachSyntax, expression: SynthesizeCall(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol)); BoundStatement disposeStmt; if (enumeratorType.IsValueType) { // No way for the struct to be nullable and disposable. Debug.Assert(((TypeSymbol)enumeratorType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T); // For non-nullable structs, no null check is required. disposeStmt = disposeCall; } else { // NB: cast to object missing from spec. Needed to ignore user-defined operators and box type parameters. // if ((object)e != null) ((IDisposable)e).Dispose(); disposeStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, left: MakeConversion( syntax: forEachSyntax, rewrittenOperand: boundEnumeratorVar, conversion: enumeratorInfo.EnumeratorConversion, rewrittenType: this.compilation.GetSpecialType(SpecialType.System_Object), @checked: false), right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: disposeCall, rewrittenAlternativeOpt: null, hasErrors: false); } finallyBlockOpt = new BoundBlock(forEachSyntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(disposeStmt)); } else { Debug.Assert(!enumeratorType.IsSealed); // IDisposable d LocalSymbol disposableVar = factory.SynthesizedLocal(idisposableTypeSymbol); // Reference to d. BoundLocal boundDisposableVar = MakeBoundLocal(forEachSyntax, disposableVar, idisposableTypeSymbol); BoundTypeExpression boundIDisposableTypeExpr = new BoundTypeExpression(forEachSyntax, aliasOpt: null, type: idisposableTypeSymbol); // e as IDisposable BoundExpression disposableVarInitValue = new BoundAsOperator(forEachSyntax, operand: boundEnumeratorVar, targetType: boundIDisposableTypeExpr, conversion: Conversion.ExplicitReference, // Explicit so the emitter won't optimize it away. type: idisposableTypeSymbol); // IDisposable d = e as IDisposable; BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue); // if (d != null) d.Dispose(); BoundStatement ifStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, // reference equality left: boundDisposableVar, right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: new BoundExpressionStatement(forEachSyntax, expression: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundDisposableVar, method: disposeMethod)), rewrittenAlternativeOpt: null, hasErrors: false); // IDisposable d = e as IDisposable; // if (d != null) d.Dispose(); finallyBlockOpt = new BoundBlock(forEachSyntax, locals: ImmutableArray.Create <LocalSymbol>(disposableVar), statements: ImmutableArray.Create <BoundStatement>(disposableVarDecl, ifStmt)); } // try { // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } // } // finally { // /* dispose of e */ // } BoundStatement tryFinally = new BoundTryStatement(forEachSyntax, tryBlock: new BoundBlock(forEachSyntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(whileLoop)), catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: finallyBlockOpt); // E e = ((C)(x)).GetEnumerator(); // try { // /* as above */ result = new BoundBlock( syntax: forEachSyntax, locals: ImmutableArray.Create(enumeratorVar), statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, tryFinally)); } else { // E e = ((C)(x)).GetEnumerator(); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } result = new BoundBlock( syntax: forEachSyntax, locals: ImmutableArray.Create(enumeratorVar), statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, whileLoop)); } AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
/// <summary> /// Produces a Try/Finally if frame has a handler (otherwise a regular block). /// Handler goes into the Finally. /// If there are nested frames, they are emitted into the try block. /// This way the handler for the current frame is guaranteed to run even if /// nested handlers throw exceptions. /// /// { /// switch(state) /// { /// case state1: /// case state2: /// case state3: /// case state4: /// try /// { /// switch(state) /// { /// case state3: /// case state4: /// try /// { /// ... more nested state dispatches if any .... /// } /// finally /// { /// // handler for a try where state3 and state4 can be observed /// handler_3_4() /// } /// break; /// } /// } /// finally /// { /// // handler for a try where state1 and state2 can be observed /// handler_1_2() /// } /// break; /// /// case state5: /// ... another dispatch of nested states to their finally blocks ... /// break; /// } /// } /// /// </summary> private BoundStatement EmitFinallyFrame(IteratorFinallyFrame frame, BoundLocal state) { BoundStatement body = null; if (frame.knownStates != null) { var breakLabel = F.GenerateLabel("break"); var sections = from ft in frame.knownStates group ft.Key by ft.Value into g select F.SwitchSection( new List<int>(g), EmitFinallyFrame(g.Key, state), F.Goto(breakLabel)); body = F.Block( F.Switch(state, sections), F.Label(breakLabel)); } if (!frame.IsRoot()) { var tryBlock = body != null ? F.Block(body) : F.Block(); body = F.Try( tryBlock, ImmutableArray<BoundCatchBlock>.Empty, F.Block(F.ExpressionStatement(F.Call(F.This(), frame.handler)))); } Debug.Assert(body != null, "we should have either sub-dispatch or a handler"); return body; }
// Used to increment integer index into an array or string. private BoundStatement MakePositionIncrement(CSharpSyntaxNode syntax, BoundLocal boundPositionVar, TypeSymbol intType) { // A normal for-loop would have a sequence point on the increment. We don't want that since the code is synthesized, // but we add a hidden sequence point to avoid disrupting the stepping experience. return new BoundSequencePoint(null, statementOpt: new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundPositionVar, right: new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.IntAddition, // unchecked, never overflows since array/string index can't be >= Int32.MaxValue left: boundPositionVar, right: MakeLiteral(syntax, constantValue: ConstantValue.Create(1), type: intType), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: intType), type: intType))); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove); MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics); if ((object)compareExchangeMethod == null) { return new BoundBlock(syntax, locals: ImmutableArray<LocalSymbol>.Empty, statements: ImmutableArray.Create<BoundStatement>( new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; } GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create<BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create<BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; return new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create<BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }; }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove); MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics); if ((object)compareExchangeMethod == null) { return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); } GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundStatement RewriteUsingStatementTryFinally(CSharpSyntaxNode syntax, BoundBlock tryBlock, BoundLocal local) { // SPEC: When ResourceType is a non-nullable value type, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when Resource type is a nullable value type or // SPEC: a reference type other than dynamic, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when ResourceType is dynamic, the expansion is: // SPEC: { // SPEC: dynamic resource = expr; // SPEC: IDisposable d = (IDisposable)resource; // SPEC: try { statement; } // SPEC: finally { if (d != null) d.Dispose(); } // SPEC: } // SPEC: // SPEC: An implementation is permitted to implement a given using statement // SPEC: differently -- for example, for performance reasons -- as long as the // SPEC: behavior is consistent with the above expansion. // // And we do in fact generate the code slightly differently than precisely how it is // described above. // // First: if the type is a non-nullable value type then we do not do the // *boxing conversion* from the resource to IDisposable. Rather, we do // a *constrained virtual call* that elides the boxing if possible. // // Now, you might wonder if that is legal; isn't skipping the boxing producing // an observable difference? Because if the value type is mutable and the Dispose // mutates it, then skipping the boxing means that we are now mutating the original, // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}" // and r is out of scope after the finally, so it is not possible to observe the mutation, // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}", // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore // it doesn't matter if we skip making *a copy of the copy*. // // This is what the dev10 compiler does, and we do so as well. // // Second: if the type is a nullable value type then we can similarly elide the boxing. // We can generate // // { // ResourceType resource = expr; // try { statement; } // finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); } // } // // Where again we do a constrained virtual call to Dispose, rather than boxing // the value to IDisposable. // // Note that this optimization is *not* what the native compiler does; in this case // the native compiler behavior is to test for HasValue, then *box* and convert // the boxed value to IDisposable. There's no need to do that. // // Third: if we have "using(x)" and x is dynamic then obviously we need not generate // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide // the completely unnecessary first temporary. BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } // local.Dispose() BoundExpression disposeCall; MethodSymbol disposeMethodSymbol; if (Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, syntax, _diagnostics, out disposeMethodSymbol)) { disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol); } else { disposeCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(disposedExpression), ErrorTypeSymbol.UnknownResultType); } // local.Dispose(); BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall); BoundExpression ifCondition; if (isNullableValueType) { // local.HasValue ifCondition = MakeNullableHasValue(syntax, local); } else if (local.Type.IsValueType) { ifCondition = null; } else { // local != null ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual); } BoundStatement finallyStatement; if (ifCondition == null) { // local.Dispose(); finallyStatement = disposeStatement; } else { // if (local != null) local.Dispose(); // or // if (local.HasValue) local.GetValueOrDefault().Dispose(); finallyStatement = RewriteIfStatement( syntax: syntax, rewrittenCondition: ifCondition, rewrittenConsequence: disposeStatement, rewrittenAlternativeOpt: null, hasErrors: false); } // try { ... } finally { if (local != null) local.Dispose(); } BoundStatement tryFinally = new BoundTryStatement( syntax: syntax, tryBlock: tryBlock, catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement)); return(tryFinally); }
public override BoundNode VisitLocal(BoundLocal node) { CheckDeclared(node.LocalSymbol); base.VisitLocal(node); return(null); }
/// <summary> /// Generates the `ValueTask<bool> MoveNextAsync()` method. /// </summary> private void GenerateIAsyncEnumeratorImplementation_MoveNextAsync() { // Produce: // if (State == StateMachineStates.FinishedStateMachine) // { // return default(ValueTask<bool>) // } // _valueOrEndPromise.Reset(); // var inst = this; // _builder.Start(ref inst); // return new ValueTask<bool>(this, _valueOrEndPromise.Version); NamedTypeSymbol IAsyncEnumeratorOfElementType = F.WellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T) .Construct(_currentField.Type.TypeSymbol); MethodSymbol IAsyncEnumerableOfElementType_MoveNextAsync = F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync) .AsMember(IAsyncEnumeratorOfElementType); // The implementation doesn't depend on the method body of the iterator method. OpenMethodImplementation(IAsyncEnumerableOfElementType_MoveNextAsync, hasMethodBodyDependency: false); var ifFinished = F.If( // if (State == StateMachineStates.FinishedStateMachine) F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)), // return default(ValueTask<bool>) thenClause: F.Return(F.Default(IAsyncEnumerableOfElementType_MoveNextAsync.ReturnType.TypeSymbol))); // _promiseOfValueOrEnd.Reset(); BoundFieldAccess promiseField = F.Field(F.This(), _promiseOfValueOrEndField); var resetMethod = (MethodSymbol)F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ManualResetValueTaskSourceLogic_T__Reset, isOptional: true) .SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol); var callReset = F.ExpressionStatement(F.Call(promiseField, resetMethod)); // _builder.Start(ref inst); Debug.Assert(!_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints); MethodSymbol startMethod = _asyncMethodBuilderMemberCollection.Start.Construct(this.stateMachineType); LocalSymbol instSymbol = F.SynthesizedLocal(this.stateMachineType); BoundLocal instLocal = F.Local(instSymbol); BoundExpressionStatement startCall = F.ExpressionStatement( F.Call( F.Field(F.This(), _builderField), startMethod, ImmutableArray.Create <BoundExpression>(instLocal))); MethodSymbol valueTask_ctor = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctor) .AsMember((NamedTypeSymbol)IAsyncEnumerableOfElementType_MoveNextAsync.ReturnType.TypeSymbol); MethodSymbol promise_get_Version = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ManualResetValueTaskSourceLogic_T__get_Version) .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol); // return new ValueTask<bool>(this, _valueOrEndPromise.Version); var returnStatement = F.Return(F.New(valueTask_ctor, F.This(), F.Call(F.Field(F.This(), _promiseOfValueOrEndField), promise_get_Version))); F.CloseMethod(F.Block( ImmutableArray.Create(instSymbol), ifFinished, callReset, // _promiseOfValueOrEnd.Reset(); F.Assignment(instLocal, F.This()), // var inst = this; startCall, // _builder.Start(ref inst); returnStatement)); }
private BoundStatement MakeSwitchStatementWithNullableExpression( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, GeneratedLabelSymbol breakLabel, BoundSwitchStatement oldNode) { Debug.Assert(rewrittenExpression.Type.IsNullableType()); var exprSyntax = rewrittenExpression.Syntax; var exprNullableType = rewrittenExpression.Type; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // Rewrite the nullable expression to a temp as we might have a user defined conversion from source expression to switch governing type. // We can avoid generating the temp if the expression is a bound local. LocalSymbol tempLocal; if (rewrittenExpression.Kind != BoundKind.Local) { BoundAssignmentOperator assignmentToTemp; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenExpression, out assignmentToTemp); var tempAssignment = new BoundExpressionStatement(exprSyntax, assignmentToTemp); statementBuilder.Add(tempAssignment); tempLocal = boundTemp.LocalSymbol; rewrittenExpression = boundTemp; } else { tempLocal = null; } // Generate a BoundConditionalGoto with null check as the conditional expression and appropriate switch label as the target: null, default or exit label. BoundStatement condGotoNullValueTargetLabel = new BoundConditionalGoto( exprSyntax, condition: MakeNullCheck(exprSyntax, rewrittenExpression, BinaryOperatorKind.NullableNullEqual), jumpIfTrue: true, label: GetNullValueTargetSwitchLabel(rewrittenSections, breakLabel)); // Rewrite the switch statement using nullable expression's underlying value as the switch expression. // rewrittenExpression.GetValueOrDefault() MethodSymbol getValueOrDefault = GetNullableMethod(syntax, exprNullableType, SpecialMember.System_Nullable_T_GetValueOrDefault); BoundCall callGetValueOrDefault = BoundCall.Synthesized(exprSyntax, rewrittenExpression, getValueOrDefault); rewrittenExpression = callGetValueOrDefault; // rewrite switch statement BoundStatement rewrittenSwitchStatement = MakeSwitchStatementWithNonNullableExpression( syntax, condGotoNullValueTargetLabel, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode); statementBuilder.Add(rewrittenSwitchStatement); return(new BoundBlock(syntax, locals: (object)tempLocal == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree())); }
private BoundExpression MakePropertyAssignment( SyntaxNode syntax, BoundExpression?rewrittenReceiver, PropertySymbol property, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, ImmutableArray <int> argsToParamsOpt, BoundExpression rewrittenRight, TypeSymbol type, bool used) { // Rewrite property assignment into call to setter. var setMethod = property.GetOwnOrInheritedSetMethod(); if ((object)setMethod == null) { var sourceProperty = (SourcePropertySymbol)property; Debug.Assert(sourceProperty.IsAutoProperty == true, "only autoproperties can be assignable without having setters"); var backingField = sourceProperty.BackingField; return(_factory.AssignmentExpression( _factory.Field(rewrittenReceiver, backingField), rewrittenRight)); } // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> argTemps; rewrittenArguments = MakeArguments( syntax, rewrittenArguments, property, setMethod, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out argTemps, invokedAsExtensionMethod: false, enableCallerInfo: ThreeState.True); if (used) { // Save expression value to a temporary before calling the // setter, and restore the temporary after the setter, so the // assignment can be used as an embedded expression. TypeSymbol?exprType = rewrittenRight.Type; Debug.Assert(exprType is object); LocalSymbol rhsTemp = _factory.SynthesizedLocal(exprType); BoundExpression boundRhs = new BoundLocal(syntax, rhsTemp, null, exprType); BoundExpression rhsAssignment = new BoundAssignmentOperator( syntax, boundRhs, rewrittenRight, exprType); BoundExpression setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rhsAssignment)); return(new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), ImmutableArray.Create(setterCall), boundRhs, type)); } else { BoundCall setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rewrittenRight)); if (argTemps.IsDefaultOrEmpty) { return(setterCall); } else { return(new BoundSequence( syntax, argTemps, ImmutableArray <BoundExpression> .Empty, setterCall, setMethod.ReturnType)); } } }
private void CheckOutVarDeclaration(BoundLocal node) { if (IsInside && !node.WasCompilerGenerated && node.Syntax.Kind() == SyntaxKind.Argument && ((ArgumentSyntax)node.Syntax).Identifier == node.LocalSymbol.IdentifierToken) { _variablesDeclared.Add(node.LocalSymbol); } }
public override BoundNode VisitLocal(BoundLocal node) { var catchFrame = this.currentAwaitCatchFrame; if (catchFrame == null || !catchFrame.hoistedLocals.ContainsKey(node.LocalSymbol)) { return base.VisitLocal(node); } var newLocal = catchFrame.hoistedLocals[node.LocalSymbol]; return node.Update(newLocal, node.ConstantValueOpt, newLocal.Type); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByValue(byType.Default, value, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878 byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { // This should not occur, as the caller will have invoked AddByNull instead. throw ExceptionUtilities.Unreachable; } if ((object)value.Value.Type == null) { return(null); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { return(null); } continue; case false: case null: continue; } } DecisionTree forType = null; // This new type test should logically be last. However it might be the same type as the one that is already // last. In that case we can produce better code by piggy-backing our new case on to the last decision. // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last // type test. for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { forType = decision; break; } else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false) { // because there is overlap, we cannot reuse some earlier entry break; } } // if we did not piggy-back, then create a new decision tree node for the type. if (forType == null) { var type = value.Value.Type; var localSymbol = PatternMatchingTemp(type); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType)); } return(AddByValue(forType, value, makeDecision)); }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.Rank == 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex0); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create <BoundExpression>(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create <BoundStatement>(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = new BoundBlock(forEachSyntax, locals: ImmutableArray.Create <LocalSymbol>(iterationVar), statements: ImmutableArray.Create <BoundStatement>(iterationVariableDecl, rewrittenBody)); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, outerLocals: ImmutableArray.Create <LocalSymbol>(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
public override BoundNode VisitLocal(BoundLocal node) { ReferenceVariable(node.Syntax, node.LocalSymbol); return(base.VisitLocal(node)); }
/// <summary> /// The rewrites are as follows: suppose the operand x is a variable of type X. The /// chosen increment/decrement operator is modelled as a static method on a type T, /// which takes a value of type T and returns the result of incrementing or decrementing /// that value. /// /// x++ /// X temp = x /// x = (X)(T.Increment((T)temp)) /// return temp /// x-- /// X temp = x /// x = (X)(T.Decrement((T)temp)) /// return temp /// ++x /// X temp = (X)(T.Increment((T)x)) /// x = temp /// return temp /// --x /// X temp = (X)(T.Decrement((T)x)) /// x = temp /// return temp /// /// Note: /// Dev11 implements dynamic prefix operators incorrectly. /// /// result = ++x.P is emitted as result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x))) /// /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument. /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for /// both dynamic and static operators. /// /// For example, we might have a class X with user-defined implicit conversions /// to and from short, but no user-defined increment or decrement operators. We /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;" /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns> public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { bool isPrefix = IsPrefix(node); bool isDynamic = node.OperatorKind.IsDynamic(); bool isChecked = node.OperatorKind.IsChecked(); ArrayBuilder<LocalSymbol> tempSymbols = ArrayBuilder<LocalSymbol>.GetInstance(); ArrayBuilder<BoundExpression> tempInitializers = ArrayBuilder<BoundExpression>.GetInstance(); CSharpSyntaxNode syntax = node.Syntax; // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols, isDynamic); TypeSymbol operandType = transformedLHS.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType); tempSymbols.Add(tempSymbol); // Not adding an entry to tempInitializers because the initial value depends on the case. BoundExpression boundTemp = new BoundLocal( syntax: syntax, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // prefix: (X)(T.Increment((T)operand))) // postfix: (X)(T.Increment((T)temp))) var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp)); // there are two strategies for completing the rewrite. // The reason is that indirect assignments read the target of the assignment before evaluating // of the assignment value and that may cause reads of operand and boundTemp to cross which // in turn would require one of them to be a real temp (not a stack local) // // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite. // // Ex: // Seq{..., operand = Seq{temp = operand + 1, temp}, ...} // instead of // Seq{.... temp = operand + 1, operand = temp, ...} // // Such rewrite will nest reads of boundTemp relative to reads of operand so both // operand and boundTemp could be optimizable (subject to all other conditions of course). // // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter // overall life time of temps and as such more appropriate. (problem of crossed reads does not affect that case) // if (IsIndirectOrInstanceField(transformedLHS)) { return RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue); } else { return RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue); } }
private BoundExpression MakeNullCoalescingOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, Conversion leftConversion, BoundNullCoalescingOperatorResultKind resultKind, TypeSymbol rewrittenResultType) { Debug.Assert(rewrittenLeft != null); Debug.Assert(rewrittenRight != null); Debug.Assert(leftConversion.IsValid); Debug.Assert((object)rewrittenResultType != null); Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); if (_inExpressionLambda) { TypeSymbol strippedLeftType = rewrittenLeft.Type.StrippedType(); Conversion rewrittenConversion = TryMakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType); if (!rewrittenConversion.Exists) { return(BadExpression(syntax, rewrittenResultType, rewrittenLeft, rewrittenRight)); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, resultKind, rewrittenResultType)); } var isUnconstrainedTypeParameter = rewrittenLeft.Type != null && !rewrittenLeft.Type.IsReferenceType && !rewrittenLeft.Type.IsValueType; // first we can make a small optimization: // If left is a constant then we already know whether it is null or not. If it is null then we // can simply generate "right". If it is not null then we can simply generate // MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime, // it can be either left or right depending on the runtime type of T if (!isUnconstrainedTypeParameter) { if (rewrittenLeft.IsDefaultValue()) { return(rewrittenRight); } if (rewrittenLeft.ConstantValue != null) { Debug.Assert(!rewrittenLeft.ConstantValue.IsNull); return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } } // string concatenation is never null. // interpolated string lowering may introduce redundant null coalescing, which we have to remove. if (IsStringConcat(rewrittenLeft)) { return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } // if left conversion is intrinsic implicit (always succeeds) and results in a reference type // we can apply conversion before doing the null check that allows for a more efficient IL emit. if (rewrittenLeft.Type.IsReferenceType && leftConversion.IsImplicit && !leftConversion.IsUserDefined) { if (!leftConversion.IsIdentity) { rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, resultKind, rewrittenResultType)); } if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable) { var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess; if (conditionalAccess != null && (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt))) { var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull); if (notNullAccess != null) { var whenNullOpt = rewrittenRight; if (whenNullOpt.Type.IsNullableType()) { notNullAccess = conditionalAccess.WhenNotNull; } if (whenNullOpt.IsDefaultValue() && whenNullOpt.Type.SpecialType != SpecialType.System_Decimal) { whenNullOpt = null; } return(conditionalAccess.Update( conditionalAccess.Receiver, conditionalAccess.HasValueMethodOpt, whenNotNull: notNullAccess, whenNullOpt: whenNullOpt, id: conditionalAccess.Id, type: rewrittenResultType )); } } } // Optimize left ?? right to left.GetValueOrDefault() when left is T? and right is the default value of T if (rewrittenLeft.Type.IsNullableType() && RemoveIdentityConversions(rewrittenRight).IsDefaultValue() && rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) && TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { return(BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault)); } // We lower left ?? right to // // var temp = left; // (temp != null) ? MakeConversion(temp) : right // BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenLeft, out tempAssignment); // temp != null BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual); // MakeConversion(temp, rewrittenResultType) BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType); Debug.Assert(convertedLeft.HasErrors || convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: nullCheck, rewrittenConsequence: convertedLeft, rewrittenAlternative: rewrittenRight, constantValueOpt: null, rewrittenType: rewrittenResultType, isRef: false); Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenResultType)); }
private BoundCatchBlock BindCatchBlock(CatchClauseSyntax node, ArrayBuilder<BoundCatchBlock> previousBlocks, DiagnosticBag diagnostics) { bool hasError = false; TypeSymbol type = null; BoundExpression boundFilter = null; var declaration = node.Declaration; if (declaration != null) { // Note: The type is being bound twice: here and in LocalSymbol.Type. Currently, // LocalSymbol.Type ignores diagnostics so it seems cleaner to bind the type here // as well. However, if LocalSymbol.Type is changed to report diagnostics, we'll // need to avoid binding here since that will result in duplicate diagnostics. type = this.BindType(declaration.Type, diagnostics); Debug.Assert((object)type != null); if (type.IsErrorType()) { hasError = true; } else { HashSet<DiagnosticInfo> useSiteDiagnostics = null; TypeSymbol effectiveType = type.EffectiveType(ref useSiteDiagnostics); if (!Compilation.IsExceptionType(effectiveType, ref useSiteDiagnostics)) { // "The type caught or thrown must be derived from System.Exception" Error(diagnostics, ErrorCode.ERR_BadExceptionType, declaration.Type); hasError = true; diagnostics.Add(declaration.Type, useSiteDiagnostics); } } } var filter = node.Filter; if (filter != null) { var filterBinder = this.GetBinder(filter); boundFilter = filterBinder.BindCatchFilter(filter, diagnostics); hasError |= boundFilter.HasAnyErrors; } if (!hasError) { // TODO: Loop is O(n), caller is O(n^2). Perhaps we could iterate in reverse order (since it's easier to find // base types than to find derived types). Debug.Assert(((object)type == null) || !type.IsErrorType()); foreach (var previousBlock in previousBlocks) { var previousType = previousBlock.ExceptionTypeOpt; // If the previous type is a generic parameter we don't know what exception types it's gonna catch exactly. // If it is a class-type we know it's gonna catch all exception types of its type and types that are derived from it. // So if the current type is a class-type (or an effective base type of a generic parameter) // that derives from the previous type the current catch is unreachable. if (previousBlock.ExceptionFilterOpt == null && (object)previousType != null && !previousType.IsErrorType()) { if ((object)type != null) { HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (Conversions.HasIdentityOrImplicitReferenceConversion(type, previousType, ref useSiteDiagnostics)) { // "A previous catch clause already catches all exceptions of this or of a super type ('{0}')" Error(diagnostics, ErrorCode.ERR_UnreachableCatch, declaration.Type, previousType); diagnostics.Add(declaration.Type, useSiteDiagnostics); hasError = true; break; } diagnostics.Add(declaration.Type, useSiteDiagnostics); } else if (previousType == Compilation.GetWellKnownType(WellKnownType.System_Exception) && Compilation.SourceAssembly.RuntimeCompatibilityWrapNonExceptionThrows) { // If the RuntimeCompatibility(WrapNonExceptionThrows = false) is applied on the source assembly or any referenced netmodule. // an empty catch may catch exceptions that don't derive from System.Exception. // "A previous catch clause already catches all exceptions..." Error(diagnostics, ErrorCode.WRN_UnreachableGeneralCatch, node.CatchKeyword); break; } } } } BoundExpression exceptionSource = null; LocalSymbol local = this.Locals.FirstOrDefault(); if ((object)local != null) { Debug.Assert(this.Locals.Length == 1); // Check for local variable conflicts in the *enclosing* binder, not the *current* binder; // obviously we will find a local of the given name in the current binder. hasError |= this.ValidateDeclarationNameConflictsInScope(local, diagnostics); exceptionSource = new BoundLocal(declaration, local, ConstantValue.NotAvailable, local.Type); } var block = this.BindBlock(node.Block, diagnostics); Debug.Assert((object)local == null || local.DeclarationKind == LocalDeclarationKind.CatchVariable); Debug.Assert((object)local == null || local.Type.IsErrorType() || (local.Type == type)); return new BoundCatchBlock(node, local, exceptionSource, type, boundFilter, block, hasError); }
/// <summary> /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit /// before and after the body, respectively. /// </summary> public override BoundNode VisitLockStatement(BoundLockStatement node) { LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax; BoundExpression rewrittenArgument = VisitExpression(node.Argument); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol argumentType = rewrittenArgument.Type; if ((object)argumentType == null) { // This isn't particularly elegant, but hopefully locking on null is // not very common. Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null); argumentType = _compilation.GetSpecialType(SpecialType.System_Object); rewrittenArgument = MakeLiteral( rewrittenArgument.Syntax, rewrittenArgument.ConstantValue, argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp. } if (argumentType.Kind == SymbolKind.TypeParameter) { // If the argument has a type parameter type, then we'll box it right away // so that the same object is passed to both Monitor.Enter and Monitor.Exit. argumentType = _compilation.GetSpecialType(SpecialType.System_Object); rewrittenArgument = MakeConversionNode( rewrittenArgument.Syntax, rewrittenArgument, Conversion.Boxing, argumentType, @checked: false, constantValueOpt: rewrittenArgument.ConstantValue); } BoundAssignmentOperator assignmentToLockTemp; BoundLocal boundLockTemp = _factory.StoreToTemp(rewrittenArgument, out assignmentToLockTemp, syntaxOpt: lockSyntax, kind: SynthesizedLocalKind.Lock); BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp); BoundExpression exitCallExpr; MethodSymbol exitMethod; if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod)) { exitCallExpr = BoundCall.Synthesized( lockSyntax, null, exitMethod, boundLockTemp); } else { exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement exitCall = new BoundExpressionStatement(lockSyntax, exitCallExpr); MethodSymbol enterMethod; if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) || TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one. enterMethod.ParameterCount == 2) { // C# 4.0+ version // L $lock = `argument`; // sequence point // bool $lockTaken = false; // try // { // Monitor.Enter($lock, ref $lockTaken); // `body` // sequence point // } // finally // { // hidden sequence point // if ($lockTaken) Monitor.Exit($lock); // } TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundAssignmentOperator assignmentToLockTakenTemp; BoundLocal boundLockTakenTemp = _factory.StoreToTemp( MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType), store: out assignmentToLockTakenTemp, syntaxOpt: lockSyntax, kind: SynthesizedLocalKind.LockTaken); BoundStatement boundLockTakenTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTakenTemp); BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp, boundLockTakenTemp)); exitCall = RewriteIfStatement( lockSyntax, boundLockTakenTemp, exitCall, null, node.HasErrors); return(new BoundBlock( lockSyntax, ImmutableArray.Create(boundLockTemp.LocalSymbol, boundLockTakenTemp.LocalSymbol), ImmutableArray.Create( InstrumentLockTargetCapture(node, boundLockTempInit), boundLockTakenTempInit, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, ImmutableArray.Create( enterCall, rewrittenBody)), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } else { // Pre-4.0 version // L $lock = `argument`; // sequence point // Monitor.Enter($lock); // NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock. // try // { // `body` // sequence point // } // finally // { // Monitor.Exit($lock); // hidden sequence point // } BoundExpression enterCallExpr; if ((object)enterMethod != null) { Debug.Assert(enterMethod.ParameterCount == 1); enterCallExpr = BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp); } else { enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, enterCallExpr); return(new BoundBlock( lockSyntax, ImmutableArray.Create(boundLockTemp.LocalSymbol), ImmutableArray.Create( InstrumentLockTargetCapture(node, boundLockTempInit), enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } }
public override BoundNode VisitLocal(BoundLocal node) { Capture(node.LocalSymbol, node.Syntax); return(base.VisitLocal(node)); }
private BoundExpression MakeNullCoalescingOperator( CSharpSyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, Conversion leftConversion, TypeSymbol rewrittenResultType) { Debug.Assert(rewrittenLeft != null); Debug.Assert(rewrittenRight != null); Debug.Assert(leftConversion.IsValid); Debug.Assert((object)rewrittenResultType != null); Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); if (_inExpressionLambda) { TypeSymbol strippedLeftType = rewrittenLeft.Type.StrippedType(); Conversion rewrittenConversion = MakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType); return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, rewrittenResultType)); } // first we can make a small optimization: // If left is a constant then we already know whether it is null or not. If it is null then we // can simply generate "right". If it is not null then we can simply generate // MakeConversion(left). if (rewrittenLeft.IsDefaultValue()) { return(rewrittenRight); } if (rewrittenLeft.ConstantValue != null) { Debug.Assert(!rewrittenLeft.ConstantValue.IsNull); return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } // if left conversion is intrinsic implicit (always succeeds) and results in a reference type // we can apply conversion before doing the null check that allows for a more efficient IL emit. if (rewrittenLeft.Type.IsReferenceType && leftConversion.IsImplicit && !leftConversion.IsUserDefined) { if (!leftConversion.IsIdentity) { rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, rewrittenResultType)); } if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable) { var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess; if (conditionalAccess != null && (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt))) { var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull); if (notNullAccess != null) { var whenNullOpt = rewrittenRight; if (whenNullOpt.Type.IsNullableType()) { notNullAccess = conditionalAccess.WhenNotNull; } if (whenNullOpt.IsDefaultValue() && whenNullOpt.Type.SpecialType != SpecialType.System_Decimal) { whenNullOpt = null; } return(conditionalAccess.Update( conditionalAccess.Receiver, conditionalAccess.HasValueMethodOpt, whenNotNull: notNullAccess, whenNullOpt: whenNullOpt, id: conditionalAccess.Id, type: rewrittenResultType )); } } } // We lower left ?? right to // // var temp = left; // (temp != null) ? MakeConversion(temp) : right // BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenLeft, out tempAssignment); // temp != null BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual); // MakeConversion(temp, rewrittenResultType) BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType); Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: nullCheck, rewrittenConsequence: convertedLeft, rewrittenAlternative: rewrittenRight, constantValueOpt: null, rewrittenType: rewrittenResultType); Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames)); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenResultType)); }
public override BoundNode VisitLocal(BoundLocal node) { if (IsInside && node.IsDeclaration) { _variablesDeclared.Add(node.LocalSymbol); } return null; }
internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular( SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, BindingDiagnosticBag diagnostics ) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember( updateMethodId ); BoundStatement @return = new BoundReturnStatement( syntax, refKind: RefKind.None, expressionOpt: null ) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add( new CSDiagnostic( new CSDiagnosticInfo( ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name ), syntax.Location ) ); return(BoundBlock.SynthesizedNoLocals(syntax, @return)); } Binder.ReportUseSite(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess( syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null ) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter( syntax, parameterSymbol: accessor.Parameters[0] ) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.System_Threading_Interlocked__CompareExchange_T ); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined( syntax, operand: BoundCall.Synthesized( syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>( boundBackingField, boundParameter ) ), conversion: Conversion.ExplicitReference, type: delegateType ); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundBackingField, right: delegateUpdate, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; return(BoundBlock.SynthesizedNoLocals( syntax, statements: ImmutableArray.Create <BoundStatement>(eventUpdate, @return) )); } compareExchangeMethod = compareExchangeMethod.Construct( ImmutableArray.Create <TypeSymbol>(delegateType) ); Binder.ReportUseSite(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal( accessor, TypeWithAnnotations.Create(delegateType), SynthesizedLocalKind.LoweringTemp ); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType) { WasCompilerGenerated = true }; } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[0], right: boundBackingField, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined( syntax, operand: BoundCall.Synthesized( syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter) ), conversion: Conversion.ExplicitReference, type: delegateType ); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized( syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>( boundBackingField, boundTmps[2], boundTmps[1] ) ); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[0], right: compareExchange, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator( syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType ) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto( syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel ) { WasCompilerGenerated = true }; return(new BoundBlock( syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return ) ) { WasCompilerGenerated = true }); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return AddByValue(byType.Default, value, makeDecision); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { return byType.Expression.ConstantValue?.IsNull == false ? null : AddByNull((DecisionTree)byType, makeDecision); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { return null; } continue; case false: case null: continue; } } DecisionTree forType = null; // Find an existing decision tree for the expression's type. Since this new test // should logically be last, we look for the last one we can piggy-back it onto. for (int i = byType.TypeAndDecision.Count - 1; i >= 0 && forType == null; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf()) { forType = decision; break; } else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false) { break; } } if (forType == null) { var type = value.Value.Type; var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(value.Value.Type, forType)); } return AddByValue(forType, value, makeDecision); }
public override BoundNode VisitLocal(BoundLocal node) { CheckReferenceToVariable(node, node.LocalSymbol); return(base.VisitLocal(node)); }
protected override void VisitLvalue(BoundLocal node) { CheckOutVarDeclaration(node); base.VisitLvalue(node); }
private BoundExpression LowerLiftedUnaryOperator( UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { // First, an optimization. If we know that the operand is always null then // we can simply lower to the alternative. BoundExpression optimized = OptimizeLiftedUnaryOperator(kind, syntax, method, loweredOperand, type); if (optimized != null) { return(optimized); } // We do not know whether the operand is null or non-null, so we generate: // // S? temp = operand; // R? r = temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(loweredOperand, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // new R?(temp.GetValueOrDefault()) BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
public override BoundNode VisitLocal(BoundLocal node) { CheckOutVarDeclaration(node); return base.VisitLocal(node); }
/// <summary> /// The rewrites are as follows: suppose the operand x is a variable of type X. The /// chosen increment/decrement operator is modelled as a static method on a type T, /// which takes a value of type T and returns the result of incrementing or decrementing /// that value. /// /// x++ /// X temp = x /// x = (X)(T.Increment((T)temp)) /// return temp /// x-- /// X temp = x /// x = (X)(T.Decrement((T)temp)) /// return temp /// ++x /// X temp = (X)(T.Increment((T)x)) /// x = temp /// return temp /// --x /// X temp = (X)(T.Decrement((T)x)) /// x = temp /// return temp /// /// Note: /// Dev11 implements dynamic prefix operators incorrectly. /// /// result = ++x.P is emitted as result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x))) /// /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument. /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for /// both dynamic and static operators. /// /// For example, we might have a class X with user-defined implicit conversions /// to and from short, but no user-defined increment or decrement operators. We /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;" /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns> public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { bool isPrefix = IsPrefix(node); bool isDynamic = node.OperatorKind.IsDynamic(); bool isChecked = node.OperatorKind.IsChecked(); ArrayBuilder <LocalSymbol> tempSymbols = ArrayBuilder <LocalSymbol> .GetInstance(); ArrayBuilder <BoundExpression> tempInitializers = ArrayBuilder <BoundExpression> .GetInstance(); CSharpSyntaxNode syntax = node.Syntax; // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols, isDynamic); TypeSymbol operandType = transformedLHS.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType); tempSymbols.Add(tempSymbol); // Not adding an entry to tempInitializers because the initial value depends on the case. BoundExpression boundTemp = new BoundLocal( syntax: syntax, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // prefix: (X)(T.Increment((T)operand))) // postfix: (X)(T.Increment((T)temp))) var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp)); // there are two strategies for completing the rewrite. // The reason is that indirect assignments read the target of the assignment before evaluating // of the assignment value and that may cause reads of operand and boundTemp to cross which // in turn would require one of them to be a real temp (not a stack local) // // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite. // // Ex: // Seq{..., operand = Seq{temp = operand + 1, temp}, ...} // instead of // Seq{.... temp = operand + 1, operand = temp, ...} // // Such rewrite will nest reads of boundTemp realtive to reads of operand so both // operand and boundTemp could be optimizable (subject to all other conditions of course). // // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter // overal life time of temps and as such more appropriate. (problem of crossed reads does not affect that case) // if (IsIndirectOrInstanceField(transformedLHS)) { return(RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue)); } else { return(RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue)); } }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, TempKind tempKind = TempKind.None) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal(containingMethod, type, syntax: (tempKind == TempKind.None) ? null : syntax, refKind: refKind, tempKind: tempKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return local; }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; CSharpSyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversion( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument)); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp( BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp, CSharpSyntaxNode syntaxOpt = null #if DEBUG , [CallerLineNumber]int callerLineNumber = 0 , [CallerFilePath]string callerFilePath = null #endif ) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal( containingMethod, type, kind, #if DEBUG createdAtLineNumber: callerLineNumber, createdAtFilePath: callerFilePath, #endif syntaxOpt: syntaxOpt ?? (kind.IsLongLived() ? syntax : null), isPinned: false, refKind: refKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return local; }
private BoundStatement UnpendBranches( AwaitFinallyFrame frame, SynthesizedLocal pendingBranchVar, SynthesizedLocal pendingException) { var parent = frame.ParentOpt; // handle proxy labels if have any var proxiedLabels = frame.proxiedLabels; var proxyLabels = frame.proxyLabels; // skip 0 - it means we took no explicit branches int i = 1; var cases = ArrayBuilder <BoundSwitchSection> .GetInstance(); if (proxiedLabels != null) { for (int cnt = proxiedLabels.Count; i <= cnt; i++) { var target = proxiedLabels[i - 1]; var parentProxy = parent.ProxyLabelIfNeeded(target); var caseStatement = _F.SwitchSection(i, _F.Goto(parentProxy)); cases.Add(caseStatement); } } if (frame.returnProxyLabel != null) { BoundLocal pendingValue = null; if (frame.returnValue != null) { pendingValue = _F.Local(frame.returnValue); } SynthesizedLocal returnValue; BoundStatement unpendReturn; var returnLabel = parent.ProxyReturnIfNeeded(_F.CurrentMethod, pendingValue, out returnValue); if (returnLabel == null) { unpendReturn = new BoundReturnStatement(_F.Syntax, pendingValue); } else { if (pendingValue == null) { unpendReturn = _F.Goto(returnLabel); } else { unpendReturn = _F.Block( _F.Assignment( _F.Local(returnValue), pendingValue), _F.Goto(returnLabel)); } } var caseStatement = _F.SwitchSection(i, unpendReturn); cases.Add(caseStatement); } return(_F.Switch(_F.Local(pendingBranchVar), cases.ToImmutableAndFree())); }
/// <summary> /// Lower a foreach loop that will enumerate a multi-dimensional array. /// /// A[...] a = x; /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...; /// for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) /// for (int p_1 = a.GetLowerBound(1); p_1 <= q_1; p_1 = p_1 + 1) /// ... /// { V v = (V)a[p_0, p_1, ...]; /* body */ } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to nested for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(!arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to // initializing each one at the corresponding level of nesting). Doing it at the same time as // the lower bound would make this code a bit simpler, but it would make it harder to compare // the roslyn and dev10 IL. // int q_0, q_1, ... LocalSymbol[] upperVar = new LocalSymbol[rank]; BoundLocal[] boundUpperVar = new BoundLocal[rank]; BoundStatement[] upperVarDecl = new BoundStatement[rank]; for (int dimension = 0; dimension < rank; dimension++) { // int q_dimension upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit); boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType); ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetUpperBound(dimension) BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int q_dimension = a.GetUpperBound(dimension); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); } // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // work from most-nested to least-nested // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(dimension) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop ? node.BreakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused // p_dimension <= q_dimension //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: boundUpperVar[dimension], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_dimension = p_dimension + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(positionVar[dimension]), rewrittenInitializer: positionVarDecl, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: body, breakLabel: breakLabel, continueLabel: continueLabel, hasErrors: node.HasErrors); } Debug.Assert(forLoop != null); BoundStatement result = new BoundBlock( forEachSyntax, ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()), ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop)); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
/// <summary> /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit /// before and after the body, respectively. /// /// C# 4.0 version /// /// L locked; /// bool flag = false; /// try { /// locked = `argument`; /// Monitor.Enter(locked, ref flag); /// `body` /// } finally { /// if (flag) Monitor.Exit(locked); /// } /// /// Pre-4.0 version /// /// L locked = `argument`; /// Monitor.Enter(locked, ref flag); /// try { /// `body` /// } finally { /// Monitor.Exit(locked); /// } /// </summary> public override BoundNode VisitLockStatement(BoundLockStatement node) { LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax; BoundExpression rewrittenArgument = VisitExpression(node.Argument); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol argumentType = rewrittenArgument.Type; if ((object)argumentType == null) { // This isn't particularly elegant, but hopefully locking on null is // not very common. Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null); argumentType = this.compilation.GetSpecialType(SpecialType.System_Object); rewrittenArgument = MakeLiteral( rewrittenArgument.Syntax, rewrittenArgument.ConstantValue, argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp. } if (argumentType.Kind == SymbolKind.TypeParameter) { // If the argument has a type parameter type, then we'll box it right away // so that the same object is passed to both Monitor.Enter and Monitor.Exit. argumentType = this.compilation.GetSpecialType(SpecialType.System_Object); rewrittenArgument = MakeConversion( rewrittenArgument.Syntax, rewrittenArgument, ConversionKind.Boxing, argumentType, @checked: false, constantValueOpt: rewrittenArgument.ConstantValue); } BoundAssignmentOperator assignmentToLockTemp; BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, tempKind: TempKind.Lock, store: out assignmentToLockTemp); BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp); if (this.generateDebugInfo) { boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point. lockSyntax, boundLockTempInit, TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End)); } BoundExpression exitCallExpr; MethodSymbol exitMethod; if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod)) { exitCallExpr = BoundCall.Synthesized( lockSyntax, null, exitMethod, boundLockTemp); } else { exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement exitCall = new BoundExpressionStatement( lockSyntax, exitCallExpr); MethodSymbol enterMethod; if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) || TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one. enterMethod.ParameterCount == 2) { // C# 4.0 version // L locked; // bool flag = false; // try { // locked = `argument`; // Monitor.Enter(locked, ref flag); // `body` // } finally { // if (flag) Monitor.Exit(locked); // } TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean); BoundAssignmentOperator assignmentToTemp; BoundLocal boundFlagTemp = this.factory.StoreToTemp( MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType), tempKind: TempKind.LockTaken, store: out assignmentToTemp); BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp); if (this.generateDebugInfo) { // hide the preamble code, we should not stop until we get to " locked = `argument`; " boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit); } BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp, boundFlagTemp)); exitCall = RewriteIfStatement( lockSyntax, ImmutableArray <LocalSymbol> .Empty, boundFlagTemp, exitCall, null, node.HasErrors); return(new BoundBlock( lockSyntax, ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol), ImmutableArray.Create <BoundStatement>( boundFlagTempInit, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } else { BoundExpression enterCallExpr; if ((object)enterMethod != null) { Debug.Assert(enterMethod.ParameterCount == 1); // Pre-4.0 version // L locked = `argument`; // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock. // try { // `body` // } finally { // Monitor.Exit(locked); // } enterCallExpr = BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp); } else { enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, enterCallExpr); return(new BoundBlock( lockSyntax, ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol), ImmutableArray.Create <BoundStatement>( boundLockTempInit, enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } }
public override BoundNode VisitLocal(BoundLocal node) { var catchFrame = _currentAwaitCatchFrame; LocalSymbol hoistedLocal; if (catchFrame == null || !catchFrame.TryGetHoistedLocal(node.LocalSymbol, out hoistedLocal)) { return base.VisitLocal(node); } return node.Update(hoistedLocal, node.ConstantValueOpt, hoistedLocal.Type); }
/// <summary> /// Prepares local variables to be used in Deconstruct call /// Adds a invocation of Deconstruct with those as out parameters onto the 'stores' sequence /// Returns the expressions for those out parameters /// </summary> private void CallDeconstruct(BoundDeconstructionAssignmentOperator node, BoundDeconstructionDeconstructStep deconstruction, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders) { Debug.Assert((object)deconstruction.DeconstructInvocationOpt != null); CSharpSyntaxNode syntax = node.Syntax; // prepare out parameters for Deconstruct var deconstructParameters = deconstruction.OutputPlaceholders; var outParametersBuilder = ArrayBuilder<BoundExpression>.GetInstance(deconstructParameters.Length); for (var i = 0; i < deconstructParameters.Length; i++) { var deconstructParameter = deconstructParameters[i]; var localSymbol = new SynthesizedLocal(_factory.CurrentMethod, deconstructParameter.Type, SynthesizedLocalKind.LoweringTemp); var localBound = new BoundLocal(syntax, localSymbol, null, deconstructParameter.Type ) { WasCompilerGenerated = true }; temps.Add(localSymbol); outParametersBuilder.Add(localBound); AddPlaceholderReplacement(deconstruction.OutputPlaceholders[i], localBound); placeholders.Add(deconstruction.OutputPlaceholders[i]); } var outParameters = outParametersBuilder.ToImmutableAndFree(); // invoke Deconstruct with placeholders replaced by locals stores.Add(VisitExpression(deconstruction.DeconstructInvocationOpt)); }
public override BoundNode VisitLocal(BoundLocal node) { ReferenceVariable(node.Syntax, node.LocalSymbol); return base.VisitLocal(node); }
private BoundStatement RewriteUsingStatementTryFinally(CSharpSyntaxNode syntax, BoundBlock tryBlock, BoundLocal local) { // SPEC: When ResourceType is a non-nullable value type, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when Resource type is a nullable value type or // SPEC: a reference type other than dynamic, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when ResourceType is dynamic, the expansion is: // SPEC: { // SPEC: dynamic resource = expr; // SPEC: IDisposable d = (IDisposable)resource; // SPEC: try { statement; } // SPEC: finally { if (d != null) d.Dispose(); } // SPEC: } // SPEC: // SPEC: An implementation is permitted to implement a given using statement // SPEC: differently -- for example, for performance reasons -- as long as the // SPEC: behavior is consistent with the above expansion. // // And we do in fact generate the code slightly differently than precisely how it is // described above. // // First: if the type is a non-nullable value type then we do not do the // *boxing conversion* from the resource to IDisposable. Rather, we do // a *constrained virtual call* that elides the boxing if possible. // // Now, you might wonder if that is legal; isn't skipping the boxing producing // an observable difference? Because if the value type is mutable and the Dispose // mutates it, then skipping the boxing means that we are now mutating the original, // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}" // and r is out of scope after the finally, so it is not possible to observe the mutation, // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}", // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore // it doesn't matter if we skip making *a copy of the copy*. // // This is what the dev10 compiler does, and we do so as well. // // Second: if the type is a nullable value type then we can similarly elide the boxing. // We can generate // // { // ResourceType resource = expr; // try { statement; } // finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); } // } // // Where again we do a constrained virtual call to Dispose, rather than boxing // the value to IDisposable. // // Note that this optimization is *not* what the native compiler does; in this case // the native compiler behavior is to test for HasValue, then *box* and convert // the boxed value to IDisposable. There's no need to do that. // // Third: if we have "using(x)" and x is dynamic then obviously we need not generate // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide // the completely unnecessary first temporary. BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } // local.Dispose() BoundExpression disposeCall; MethodSymbol disposeMethodSymbol; if (TryGetSpecialTypeMember(syntax, SpecialMember.System_IDisposable__Dispose, out disposeMethodSymbol)) { disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol); } else { disposeCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(disposedExpression), ErrorTypeSymbol.UnknownResultType); } // local.Dispose(); BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall); BoundExpression ifCondition; if (isNullableValueType) { MethodSymbol hasValue = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_get_HasValue); // local.HasValue ifCondition = BoundCall.Synthesized(syntax, local, hasValue); } else if (local.Type.IsValueType) { ifCondition = null; } else { // local != null ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual); } BoundStatement finallyStatement; if (ifCondition == null) { // local.Dispose(); finallyStatement = disposeStatement; } else { // if (local != null) local.Dispose(); // or // if (local.HasValue) local.GetValueOrDefault().Dispose(); finallyStatement = RewriteIfStatement( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, rewrittenCondition: ifCondition, rewrittenConsequence: disposeStatement, rewrittenAlternativeOpt: null, hasErrors: false); } // try { ... } finally { if (local != null) local.Dispose(); } BoundStatement tryFinally = new BoundTryStatement( syntax: syntax, tryBlock: tryBlock, catchBlocks: ImmutableArray<BoundCatchBlock>.Empty, finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement)); return tryFinally; }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal(containingMethod, type, kind, syntax: kind.IsLongLived() ? syntax : null, refKind: refKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return local; }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = factory.SynthesizedLocal(stringType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // int p; LocalSymbol positionVar = factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex0); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create <BoundStatement>(stringVarDecl, positionVariableDecl)); MethodSymbol method = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: method, arguments: ImmutableArray <BoundExpression> .Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.IsValid); // (V)s.Chars[p] MethodSymbol chars = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars); BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: chars, arguments: ImmutableArray.Create <BoundExpression>(boundPositionVar)), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = new BoundBlock(forEachSyntax, locals: ImmutableArray.Create <LocalSymbol>(iterationVar), statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody)); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
public override BoundNode VisitLocal(BoundLocal node) { Capture(node.LocalSymbol, node.Syntax); return base.VisitLocal(node); }
/// <summary> /// Lower a foreach loop that will enumerate a multi-dimensional array. /// /// A[...] a = x; /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...; /// for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) /// for (int p_1 = a.GetLowerBound(1); p_1 <= q_1; p_1 = p_1 + 1) /// ... /// { V v = (V)a[p_0, p_1, ...]; /* body */ } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to nested for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to // initializing each one at the corresponding level of nesting). Doing it at the same time as // the lower bound would make this code a bit simpler, but it would make it harder to compare // the roslyn and dev10 IL. // int q_0, q_1, ... LocalSymbol[] upperVar = new LocalSymbol[rank]; BoundLocal[] boundUpperVar = new BoundLocal[rank]; BoundStatement[] upperVarDecl = new BoundStatement[rank]; for (int dimension = 0; dimension < rank; dimension++) { // int q_/*dimension*/ upperVar[dimension] = factory.SynthesizedLocal( intType, syntax: forEachSyntax, kind: (SynthesizedLocalKind)((int)SynthesizedLocalKind.ForEachArrayLimit0 + dimension)); boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType); ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create <BoundExpression>( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetUpperBound(/*dimension*/) BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int q_/*dimension*/ = a.GetUpperBound(/*dimension*/); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); } // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = factory.SynthesizedLocal( intType, syntax: forEachSyntax, kind: (SynthesizedLocalKind)((int)SynthesizedLocalKind.ForEachArrayIndex0 + dimension)); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create <BoundExpression>((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, locals: ImmutableArray.Create <LocalSymbol>(iterationVar), statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody)); // work from most-nested to least-nested // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create <BoundExpression>( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop ? node.BreakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused // p_/*dimension*/ <= q_/*dimension*/ //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: boundUpperVar[dimension], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(positionVar[dimension]), rewrittenInitializer: positionVarDecl, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: body, breakLabel: breakLabel, continueLabel: continueLabel, hasErrors: node.HasErrors); } Debug.Assert(forLoop != null); BoundStatement result = new BoundBlock( forEachSyntax, ImmutableArray.Create <LocalSymbol>(arrayVar).Concat(upperVar.AsImmutableOrNull()), ImmutableArray.Create <BoundStatement>(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop)); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
/// <summary> /// Lower "using (ResourceType resource = expression) statement" to a try-finally block. /// </summary> /// <remarks> /// Assumes that the local symbol will be declared (i.e. in the LocalsOpt array) of an enclosing block. /// Assumes that using statements with multiple locals have already been split up into multiple using statements. /// </remarks> private BoundBlock RewriteDeclarationUsingStatement(CSharpSyntaxNode usingSyntax, BoundLocalDeclaration localDeclaration, BoundBlock tryBlock, Conversion idisposableConversion) { CSharpSyntaxNode declarationSyntax = localDeclaration.Syntax; LocalSymbol localSymbol = localDeclaration.LocalSymbol; TypeSymbol localType = localSymbol.Type; Debug.Assert((object)localType != null); //otherwise, there wouldn't be a conversion to IDisposable BoundLocal boundLocal = new BoundLocal(declarationSyntax, localSymbol, localDeclaration.InitializerOpt.ConstantValue, localType); BoundStatement rewrittenDeclaration = (BoundStatement)Visit(localDeclaration); // If we know that the expression is null, then we know that the null check in the finally block // will fail, and the Dispose call will never happen. That is, the finally block will have no effect. // Consequently, we can simply skip the whole try-finally construct and just create a block containing // the new declaration. if (boundLocal.ConstantValue == ConstantValue.Null) { //localSymbol will be declared by an enclosing block return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryBlock); } if (localType.IsDynamic()) { BoundExpression tempInit = MakeConversion( declarationSyntax, boundLocal, idisposableConversion, compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); return new BoundBlock( syntax: usingSyntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), //localSymbol will be declared by an enclosing block statements: ImmutableArray.Create<BoundStatement>( rewrittenDeclaration, new BoundExpressionStatement(declarationSyntax, tempAssignment), tryFinally)); } else { BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundLocal); // localSymbol will be declared by an enclosing block return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryFinally); } }
private void CheckOutVarDeclaration(BoundLocal node) { if (IsInside && !node.WasCompilerGenerated && node.Syntax.Kind() == SyntaxKind.DeclarationExpression) { var declaration = (DeclarationExpressionSyntax)node.Syntax; if (declaration.Designation.Kind() == SyntaxKind.SingleVariableDesignation && ((SingleVariableDesignationSyntax)declaration.Designation).Identifier == node.LocalSymbol.IdentifierToken && declaration.Parent != null && declaration.Parent.Kind() == SyntaxKind.Argument && ((ArgumentSyntax)declaration.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword) { _variablesDeclared.Add(node.LocalSymbol); } } }
/// <summary> /// Called when a local represents an out variable declaration. Its syntax is of type DeclarationExpressionSyntax. /// </summary> private void CheckOutDeclaration(BoundLocal local, Symbol method) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsOutVariable, local); } }
private BoundExpression MakeNullCoalescingOperator( CSharpSyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, Conversion leftConversion, TypeSymbol rewrittenResultType) { Debug.Assert(rewrittenLeft != null); Debug.Assert(rewrittenRight != null); Debug.Assert(leftConversion.IsValid); Debug.Assert((object)rewrittenResultType != null); Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, ignoreDynamic: true)); if (inExpressionLambda) { TypeSymbol strippedLeftType = rewrittenLeft.Type.StrippedType(); Conversion rewrittenConversion = MakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType); return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, rewrittenResultType)); } // first we can make a small optimization: ConstantValue leftConstantValue = rewrittenLeft.ConstantValue; if (leftConstantValue != null) { // If left is a constant then we already know whether it is null or not. If it is null then we // can simply generate "right". If it is not null then we can simply generate // MakeConversion(left). return(leftConstantValue.IsNull ? rewrittenRight : GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } // if left conversion is intrinsic implicit (always succeeds) and results in a reference type // we can apply conversion before doing the null check that allows for a more efficient IL emit. if (rewrittenLeft.Type.IsReferenceType && leftConversion.IsImplicit && !leftConversion.IsUserDefined) { if (!leftConversion.IsIdentity) { rewrittenLeft = MakeConversion(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, rewrittenResultType)); } // We lower left ?? right to // // var temp = left; // (temp != null) ? MakeConversion(temp) : right // BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = factory.StoreToTemp(rewrittenLeft, out tempAssignment); // temp != null BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual); // MakeConversion(temp, rewrittenResultType) BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType); Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, ignoreDynamic: true)); // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: nullCheck, rewrittenConsequence: convertedLeft, rewrittenAlternative: rewrittenRight, constantValueOpt: null, rewrittenType: rewrittenResultType); Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, ignoreDynamic: true)); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenResultType)); }