// All nodes with a single child expression are rewritten in the same manner. // // If the node is an expression and the child contains an await expression, then the rewriting transforms: // Expression(Spill(sideEffects, Value)) // to: // Spill(sideEffects, Expression(Value)) // // If the node is a statement and the child contains an await expression, then the rewriting transforms: // ExpressionStatement(Spill(sideEffects, Value)) // to: // Block(sideEffects, ExpressionStatement(Value)) // // If the child expression does not contain an await expression then no rewriting is performed. public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { BoundExpression expression = (BoundExpression)this.Visit(node.Expression); if (expression.Kind != BoundKind.SpillSequence) { return node.Update(expression); } return RewriteSpillSequenceAsBlock((BoundSpillSequence)expression); }
private BoundStatement RewriteLocalDeclaration(BoundLocalDeclaration originalOpt, SyntaxNode syntax, LocalSymbol localSymbol, BoundExpression rewrittenInitializer, bool hasErrors = false) { // A declaration of a local variable without an initializer has no associated IL. // Simply remove the declaration from the bound tree. The local symbol will // remain in the bound block, so codegen will make a stack frame location for it. if (rewrittenInitializer == null) { return null; } // A declaration of a local constant also does nothing, even though there is // an assignment. The value will be emitted directly where it is used. The // local symbol remains in the bound block, but codegen will skip making a // stack frame location for it. (We still need a symbol for it to stay // around because we'll be generating debug info for it.) if (localSymbol.IsConst) { if (!localSymbol.Type.IsReferenceType && localSymbol.ConstantValue == null) { // This can occur in error scenarios (e.g. bad imported metadata) hasErrors = true; } else { return null; } } // lowered local declaration node is associated with declaration (not whole statement) // this is done to make sure that debugger stepping is same as before var localDeclaration = syntax as LocalDeclarationStatementSyntax; if (localDeclaration != null) { syntax = localDeclaration.Declaration.Variables[0]; } BoundStatement rewrittenLocalDeclaration = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, new BoundLocal( syntax, localSymbol, null, localSymbol.Type ), rewrittenInitializer, localSymbol.Type, localSymbol.RefKind), hasErrors); return InstrumentLocalDeclarationIfNecessary(originalOpt, localSymbol, rewrittenLocalDeclaration); }
internal static ImmutableArray<BoundStatement> ConstructScriptConstructorBody( BoundStatement loweredBody, MethodSymbol constructor, SynthesizedSubmissionFields previousSubmissionFields, CSharpCompilation compilation) { // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance. // // Unlike regular field initializers, initializers of global script variables can access "this" instance. // If the base class had a constructor that initializes its state a global variable would access partially initialized object. // For this reason Script class must always derive directly from a class that has no state (System.Object). CSharpSyntaxNode syntax = loweredBody.Syntax; // base constructor call: Debug.Assert((object)constructor.ContainingType.BaseTypeNoUseSiteDiagnostics == null || constructor.ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object); var objectType = constructor.ContainingAssembly.GetSpecialType(SpecialType.System_Object); BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true }; BoundStatement baseConstructorCall = new BoundExpressionStatement(syntax, new BoundCall(syntax, receiverOpt: receiver, method: objectType.InstanceConstructors[0], arguments: ImmutableArray<BoundExpression>.Empty, argumentNamesOpt: ImmutableArray<string>.Empty, argumentRefKindsOpt: ImmutableArray<RefKind>.Empty, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: ImmutableArray<int>.Empty, resultKind: LookupResultKind.Viable, type: objectType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; var statements = ArrayBuilder<BoundStatement>.GetInstance(); statements.Add(baseConstructorCall); if (constructor.IsSubmissionConstructor) { // submission initialization: MakeSubmissionInitialization(statements, syntax, constructor, previousSubmissionFields, compilation); } statements.Add(loweredBody); return statements.ToImmutableAndFree(); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { var syntax = node.Syntax; var loweredExpression = VisitUnusedExpression(node.Expression); if (loweredExpression == null) { // NOTE: not using a BoundNoOpStatement, since we don't want a nop to be emitted. // CONSIDER: could use a BoundNoOpStatement (DevDiv #12943). return BoundStatementList.Synthesized(syntax); } else { return AddSequencePoint(node.Update(loweredExpression)); } }
public override BoundStatement InstrumentFieldOrPropertyInitializer(BoundExpressionStatement original, BoundStatement rewritten) { rewritten = base.InstrumentExpressionStatement(original, rewritten); CSharpSyntaxNode syntax = original.Syntax; switch (syntax.Parent.Parent.Kind()) { case SyntaxKind.VariableDeclarator: var declaratorSyntax = (VariableDeclaratorSyntax)syntax.Parent.Parent; return AddSequencePoint(declaratorSyntax, rewritten); case SyntaxKind.PropertyDeclaration: var declaration = (PropertyDeclarationSyntax)syntax.Parent.Parent; return AddSequencePoint(declaration, rewritten); default: throw ExceptionUtilities.UnexpectedValue(syntax.Parent.Parent.Kind()); } }
private BoundStatement RewriteExpressionStatement(BoundExpressionStatement node, bool suppressInstrumentation = false) { var loweredExpression = VisitUnusedExpression(node.Expression); if (loweredExpression == null) { return null; } else { BoundStatement result = node.Update(loweredExpression); if (!suppressInstrumentation && this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentExpressionStatement(node, result); } return result; } }
public override BoundStatement InstrumentExpressionStatement(BoundExpressionStatement original, BoundStatement rewritten) { rewritten = base.InstrumentExpressionStatement(original, rewritten); if (original.IsConstructorInitializer()) { switch (original.Syntax.Kind()) { case SyntaxKind.ConstructorDeclaration: // This is an implicit constructor initializer. var decl = (ConstructorDeclarationSyntax)original.Syntax; return new BoundSequencePointWithSpan(decl, rewritten, CreateSpanForConstructorInitializer(decl)); case SyntaxKind.BaseConstructorInitializer: case SyntaxKind.ThisConstructorInitializer: var init = (ConstructorInitializerSyntax)original.Syntax; return new BoundSequencePointWithSpan(init, rewritten, CreateSpanForConstructorInitializer((ConstructorDeclarationSyntax)init.Parent)); } } return AddSequencePoint(rewritten); }
public override BoundNode VisitLocalDeconstructionDeclaration(BoundLocalDeconstructionDeclaration node) { var syntax = node.Syntax; var loweredExpression = VisitUnusedExpression(node.Assignment); if (loweredExpression == null) { // NOTE: not using a BoundNoOpStatement, since we don't want a nop to be emitted. // CONSIDER: could use a BoundNoOpStatement (DevDiv #12943). return BoundStatementList.Synthesized(syntax); } else { BoundStatement result = new BoundExpressionStatement(loweredExpression.Syntax, loweredExpression, node.HasErrors); result.WasCompilerGenerated = node.WasCompilerGenerated; if (this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentLocalDeconstructionDeclaration(node, result); } return result; } }
private static BoundInitializer BindGlobalStatement( Binder binder, SynthesizedInteractiveInitializerMethod scriptInitializer, StatementSyntax statementNode, DiagnosticBag diagnostics, bool isLast) { var statement = binder.BindStatement(statementNode, diagnostics); if (isLast && !statement.HasAnyErrors) { // the result of the last global expression is assigned to the result storage for submission result: if (binder.Compilation.IsSubmission) { // insert an implicit conversion for the submission return type (if needed): var expression = InitializerRewriter.GetTrailingScriptExpression(statement); if (expression != null && ((object)expression.Type == null || expression.Type.SpecialType != SpecialType.System_Void)) { var submissionResultType = scriptInitializer.ResultType; expression = binder.GenerateConversionForAssignment(submissionResultType, expression, diagnostics); statement = new BoundExpressionStatement(statement.Syntax, expression, expression.HasErrors); } } // don't allow trailing expressions after labels (as in regular C#, labels must be followed by a statement): if (statement.Kind == BoundKind.LabeledStatement) { var labeledStatementBody = ((BoundLabeledStatement)statement).Body; while (labeledStatementBody.Kind == BoundKind.LabeledStatement) { labeledStatementBody = ((BoundLabeledStatement)labeledStatementBody).Body; } if (InitializerRewriter.GetTrailingScriptExpression(labeledStatementBody) != null) { Error(diagnostics, ErrorCode.ERR_SemicolonExpected, ((ExpressionStatementSyntax)labeledStatementBody.Syntax).SemicolonToken); } } } return new BoundGlobalStatementInitializer(statementNode, statement); }
public override BoundStatement InstrumentFieldOrPropertyInitializer(BoundExpressionStatement original, BoundStatement rewritten) { return Previous.InstrumentFieldOrPropertyInitializer(original, rewritten); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { if (node.Expression.Kind == BoundKind.AwaitExpression) { return VisitAwaitExpression((BoundAwaitExpression)node.Expression, resultPlace: null); } else if (node.Expression.Kind == BoundKind.AssignmentOperator) { var expression = (BoundAssignmentOperator)node.Expression; if (expression.Right.Kind == BoundKind.AwaitExpression) { return VisitAwaitExpression((BoundAwaitExpression)expression.Right, resultPlace: expression.Left); } } BoundExpression expr = (BoundExpression)this.Visit(node.Expression); return (expr != null) ? node.Update(expr) : (BoundStatement)F.Block(); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { // expressions cannot contain labels, branches or yields. return(null); }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray<BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count]; var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission ); var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission ); // TODO: report missing adder/getter Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null); var intType = compilation.GetSpecialType(SpecialType.System_Int32); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; int i = 0; // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this); var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); BoundExpression setSubmission = BoundCall.Synthesized(syntax, null, submissionAdder, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, thisReference ); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { setSubmission = new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, setSubmission, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type ) { WasCompilerGenerated = true }; } result[i++] = new BoundExpressionStatement(syntax, setSubmission) { WasCompilerGenerated = true }; foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptClass = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionId >= 0); // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>); result[i++] = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, BoundCall.Synthesized(syntax, null, submissionGetter, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType) { WasCompilerGenerated = true }), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptClass ), targetScriptClass ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } Debug.Assert(i == result.Length); return result.AsImmutableOrNull(); }
/// <summary> /// Wrap a given expression e into a block as either { e; } or { return e; } /// Shared between lambda and expression-bodied method binding. /// </summary> internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableArray<LocalSymbol> locals, ExpressionSyntax expressionSyntax, BoundExpression expression, DiagnosticBag diagnostics) { var returnType = GetCurrentReturnType(); var syntax = expressionSyntax ?? expression.Syntax; BoundStatement statement; if ((object)returnType != null) { if (returnType.SpecialType == SpecialType.System_Void || IsTaskReturningAsyncMethod()) { // If the return type is void then the expression is required to be a legal // statement expression. Debug.Assert(expressionSyntax != null || !IsValidStatementExpression(expression.Syntax, expression)); bool errors = false; if (expressionSyntax == null || !IsValidStatementExpression(expressionSyntax, expression)) { Error(diagnostics, ErrorCode.ERR_IllegalStatement, syntax); errors = true; } // Don't mark compiler generated so that the rewriter generates sequence points var expressionStatement = new BoundExpressionStatement(syntax, expression, errors); CheckForUnobservedAwaitable(expressionStatement, diagnostics); statement = expressionStatement; } else { expression = CreateReturnConversion(syntax, diagnostics, expression, returnType); statement = new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; } } else if (expression.Type?.SpecialType == SpecialType.System_Void) { statement = new BoundExpressionStatement(syntax, expression) { WasCompilerGenerated = true }; } else { statement = new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; } // Need to attach the tree for when we generate sequence points. return new BoundBlock(node, locals, ImmutableArray.Create(statement)) { WasCompilerGenerated = node.Kind() != SyntaxKind.ArrowExpressionClause }; }
public virtual BoundStatement InstrumentFieldOrPropertyInitializer(BoundExpressionStatement original, BoundStatement rewritten) { Debug.Assert(LocalRewriter.IsFieldOrPropertyInitializer(original)); return(InstrumentStatement(original, rewritten)); }
private BoundStatement MakeSwitchStatementWithNullableExpression( SyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, ImmutableArray <LocalFunctionSymbol> localFunctions, 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 = UnsafeGetNullableMethod(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, localFunctions, breakLabel, oldNode); statementBuilder.Add(rewrittenSwitchStatement); return(new BoundBlock( syntax, locals: (object)tempLocal == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree())); }
public virtual BoundStatement InstrumentExpressionStatement(BoundExpressionStatement original, BoundStatement rewritten) { return(InstrumentStatement(original, rewritten)); }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count]; var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission ); var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission ); // TODO: report missing adder/getter Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null); var intType = compilation.GetSpecialType(SpecialType.System_Int32); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; int i = 0; // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this); var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); BoundExpression setSubmission = BoundCall.Synthesized(syntax, null, submissionAdder, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, thisReference ); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { setSubmission = new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, setSubmission, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type ) { WasCompilerGenerated = true }; } result[i++] = new BoundExpressionStatement(syntax, setSubmission) { WasCompilerGenerated = true }; foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptClass = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionId >= 0); // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>); result[i++] = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, BoundCall.Synthesized(syntax, null, submissionGetter, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType) { WasCompilerGenerated = true }), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptClass ), targetScriptClass ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } Debug.Assert(i == result.Length); return(result.AsImmutableOrNull()); }
internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block) { var syntax = block.Syntax; Debug.Assert(method.MethodKind == MethodKind.Destructor); Debug.Assert(syntax.Kind() == SyntaxKind.Block || syntax.Kind() == SyntaxKind.ArrowExpressionClause); // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact // requirements), then we need to call that method in a finally block. Otherwise, just return block as-is. // NOTE: the Finalize method need not be a destructor or be overridden by the current method. MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method); if ((object)baseTypeFinalize != null) { BoundStatement baseFinalizeCall = new BoundExpressionStatement( syntax, BoundCall.Synthesized( syntax, new BoundBaseReference( syntax, method.ContainingType) { WasCompilerGenerated = true }, baseTypeFinalize)) { WasCompilerGenerated = true }; if (syntax.Kind() == SyntaxKind.Block) { //sequence point to mimic Dev10 baseFinalizeCall = new BoundSequencePointWithSpan( syntax, baseFinalizeCall, ((BlockSyntax)syntax).CloseBraceToken.Span); } return(new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundStatement>( new BoundTryStatement( syntax, block, ImmutableArray <BoundCatchBlock> .Empty, new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundStatement>( baseFinalizeCall) ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }))); } return(block); }
/// <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, localsOpt: default(ImmutableArray <LocalSymbol>), 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); 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, localsOpt: tmps.AsImmutableOrNull(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
/// <summary> /// Construct a body for an auto-property accessor (updating or returning the backing field). /// </summary> internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMethodSymbol accessor) { Debug.Assert(accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet); var property = (SourcePropertySymbol)accessor.AssociatedSymbol; CSharpSyntaxNode syntax = property.CSharpSyntaxNode; BoundExpression thisReference = null; if (!accessor.IsStatic) { var thisSymbol = accessor.ThisParameter; thisReference = new BoundThisReference(syntax, thisSymbol.Type) { WasCompilerGenerated = true }; } var field = property.BackingField; var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }; BoundStatement statement; if (accessor.MethodKind == MethodKind.PropertyGet) { statement = new BoundReturnStatement(syntax, fieldAccess) { WasCompilerGenerated = true }; } else { Debug.Assert(accessor.MethodKind == MethodKind.PropertySet); var parameter = accessor.Parameters[0]; statement = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, fieldAccess, new BoundParameter(syntax, parameter) { WasCompilerGenerated = true }, property.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } statement = new BoundSequencePoint(accessor.SyntaxNode, statement) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, default(ImmutableArray <LocalSymbol>), ImmutableArray.Create <BoundStatement>(statement)) { WasCompilerGenerated = true }); }
/// <summary> /// Lower "using (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { return(tryBlock); } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversion( expressionSyntax, rewrittenExpression, node.IDisposableConversion.Kind, this.compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); } else { // ResourceType temp = expr; boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression. expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, localsOpt: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
internal static BoundBlock ConstructAutoPropertyAccessorBody( SourceMemberMethodSymbol accessor ) { Debug.Assert( accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet ); var property = (SourcePropertySymbolBase)accessor.AssociatedSymbol; CSharpSyntaxNode syntax = property.CSharpSyntaxNode; BoundExpression thisReference = null; if (!accessor.IsStatic) { var thisSymbol = accessor.ThisParameter; thisReference = new BoundThisReference(syntax, thisSymbol.Type) { WasCompilerGenerated = true }; } var field = property.BackingField; var fieldAccess = new BoundFieldAccess( syntax, thisReference, field, ConstantValue.NotAvailable ) { WasCompilerGenerated = true }; BoundStatement statement; if (accessor.MethodKind == MethodKind.PropertyGet) { statement = new BoundReturnStatement( accessor.SyntaxNode, RefKind.None, fieldAccess ); } else { Debug.Assert(accessor.MethodKind == MethodKind.PropertySet); var parameter = accessor.Parameters[0]; statement = new BoundExpressionStatement( accessor.SyntaxNode, new BoundAssignmentOperator( syntax, fieldAccess, new BoundParameter(syntax, parameter) { WasCompilerGenerated = true }, property.Type ) { WasCompilerGenerated = true } ); } return(BoundBlock.SynthesizedNoLocals(syntax, statement)); }
/// <summary> /// Generate a thread-safe accessor for a WinRT field-like event. /// /// Add: /// return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddEventHandler(value); /// /// Remove: /// EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveEventHandler(value); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; Debug.Assert((object)accessor != null); FieldSymbol field = eventSymbol.AssociatedField; Debug.Assert((object)field != null); NamedTypeSymbol fieldType = (NamedTypeSymbol)field.Type; Debug.Assert(fieldType.Name == "EventRegistrationTokenTable"); MethodSymbol getOrCreateMethod = (MethodSymbol)Binder.GetWellKnownTypeMember( compilation, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__GetOrCreateEventRegistrationTokenTable, diagnostics, syntax: syntax); if ((object)getOrCreateMethod == null) { Debug.Assert(diagnostics.HasAnyErrors()); return(null); } getOrCreateMethod = getOrCreateMethod.AsMember(fieldType); WellKnownMember processHandlerMember = isAddMethod ? WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__AddEventHandler : WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__RemoveEventHandler; MethodSymbol processHandlerMethod = (MethodSymbol)Binder.GetWellKnownTypeMember( compilation, processHandlerMember, diagnostics, syntax: syntax); if ((object)processHandlerMethod == null) { Debug.Assert(diagnostics.HasAnyErrors()); return(null); } processHandlerMethod = processHandlerMethod.AsMember(fieldType); // _tokenTable BoundFieldAccess fieldAccess = new BoundFieldAccess( syntax, field.IsStatic ? null : new BoundThisReference(syntax, accessor.ThisParameter.Type), field, constantValueOpt: null) { WasCompilerGenerated = true }; // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable) BoundCall getOrCreateCall = BoundCall.Synthesized( syntax, receiverOpt: null, method: getOrCreateMethod, arg0: fieldAccess); // value BoundParameter parameterAccess = new BoundParameter( syntax, accessor.Parameters[0]); // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value) // or RemoveHandler BoundCall processHandlerCall = BoundCall.Synthesized( syntax, receiverOpt: getOrCreateCall, method: processHandlerMethod, arg0: parameterAccess); if (isAddMethod) { // { // return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value); // } BoundStatement returnStatement = BoundReturnStatement.Synthesized(syntax, RefKind.None, processHandlerCall); return(BoundBlock.SynthesizedNoLocals(syntax, returnStatement)); } else { // { // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveHandler(value); // return; // } BoundStatement callStatement = new BoundExpressionStatement(syntax, processHandlerCall); BoundStatement returnStatement = new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null); return(BoundBlock.SynthesizedNoLocals(syntax, callStatement, returnStatement)); } }
private static BoundStatement RewriteFieldInitializer(BoundFieldInitializer fieldInit) { var syntax = fieldInit.Syntax; var boundReceiver = fieldInit.Field.IsStatic ? null : new BoundThisReference(syntax, fieldInit.Field.ContainingType); // Mark this as CompilerGenerated so that the local rewriter doesn't add a sequence point. BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null), fieldInit.InitialValue, fieldInit.Field.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; Debug.Assert(syntax is ExpressionSyntax); // Should be the initial value. Debug.Assert(syntax.Parent.Kind == SyntaxKind.EqualsValueClause); switch (syntax.Parent.Parent.Kind) { case SyntaxKind.VariableDeclarator: var declaratorSyntax = (VariableDeclaratorSyntax)syntax.Parent.Parent; boundStatement = LocalRewriter.AddSequencePoint(declaratorSyntax, boundStatement); break; case SyntaxKind.PropertyDeclaration: var declaration = (PropertyDeclarationSyntax)syntax.Parent.Parent; boundStatement = LocalRewriter.AddSequencePoint(declaration, boundStatement); break; default: throw ExceptionUtilities.Unreachable; } return boundStatement; }
/// <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); /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= /// /// </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); 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.ReportUseSiteDiagnostics(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.ReportUseSiteDiagnostics(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, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } // 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(), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
/// <summary> /// Report an error if this is an awaitable async method invocation that is not being awaited. /// </summary> /// <remarks> /// The checks here are equivalent to StatementBinder::CheckForUnobservedAwaitable() in the native compiler. /// </remarks> private void CheckForUnobservedAwaitable(BoundExpressionStatement expressionStatement, DiagnosticBag diagnostics) { if (expressionStatement == null) { return; } BoundExpression expression = expressionStatement.Expression; // If we don't have an expression or it doesn't have a type, just bail out // now. Also, the dynamic type is always awaitable in an async method and // could generate a lot of noise if we warned on it. Finally, we only want // to warn on method calls, not other kinds of expressions. if (expression == null || expression.Kind != BoundKind.Call || (object)expression.Type == null || expression.Type.IsDynamic() || expression.Type.SpecialType == SpecialType.System_Void) { return; } var call = (BoundCall)expression; // First check if the target method is async. if ((object)call.Method != null && call.Method.IsAsync) { Error(diagnostics, ErrorCode.WRN_UnobservedAwaitableExpression, expression.Syntax); return; } // Then check if the method call returns a WinRT async type. if (ImplementsWinRTAsyncInterface(call.Type)) { Error(diagnostics, ErrorCode.WRN_UnobservedAwaitableExpression, expression.Syntax); return; } // Finally, if we're in an async method, and the expression could be be awaited, report that it is instead discarded. if (CouldBeAwaited(expression)) { Error(diagnostics, ErrorCode.WRN_UnobservedAwaitableExpression, expression.Syntax); } }
public override BoundStatement InstrumentExpressionStatement(BoundExpressionStatement original, BoundStatement rewritten) { return(Previous.InstrumentExpressionStatement(original, rewritten)); }
public override BoundStatement InstrumentFieldOrPropertyInitializer(BoundExpressionStatement original, BoundStatement rewritten) { return(AddDynamicAnalysis(original, base.InstrumentExpressionStatement(original, rewritten))); }
public override BoundStatement InstrumentFieldOrPropertyInitializer(BoundExpressionStatement original, BoundStatement rewritten) { return(Previous.InstrumentFieldOrPropertyInitializer(original, rewritten)); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { // NOTE: not using a BoundNoOpStatement, since we don't want a nop to be emitted. // CONSIDER: could use a BoundNoOpStatement (DevDiv #12943). return(RewriteExpressionStatement(node) ?? BoundStatementList.Synthesized(node.Syntax)); }
/// <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)); }
public override BoundStatement InstrumentExpressionStatement(BoundExpressionStatement original, BoundStatement rewritten) { return Previous.InstrumentExpressionStatement(original, rewritten); }
/// <summary> /// Lower "using [await] (expression) statement" to a try-finally block. /// </summary> private BoundBlock MakeExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator. return(tryBlock); } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; SyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; // or // IAsyncDisposable temp = (IAsyncDisposable) expr; TypeSymbol iDisposableType = node.AwaitOpt is null? _compilation.GetSpecialType(SpecialType.System_IDisposable) : _compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable); BoundExpression tempInit = MakeConversionNode( expressionSyntax, rewrittenExpression, Conversion.GetTrivialConversion(node.IDisposableConversion.Kind), iDisposableType, @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = _factory.StoreToTemp(tempInit, out tempAssignment, kind: SynthesizedLocalKind.Using); } else { // ResourceType temp = expr; boundTemp = _factory.StoreToTemp(rewrittenExpression, out tempAssignment, syntaxOpt: usingSyntax, kind: SynthesizedLocalKind.Using); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.Instrument) { expressionStatement = _instrumenter.InstrumentUsingTargetCapture(node, expressionStatement); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp, usingSyntax.AwaitKeyword, node.AwaitOpt, node.DisposeMethodOpt); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, locals: node.Locals.Add(boundTemp.LocalSymbol), statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
/// <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 */ // } var rewrittenBodyBlock = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); BoundStatement whileLoop = RewriteWhileStatement( syntax: forEachSyntax, rewrittenCondition: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.MoveNextMethod), conditionSequencePointSpan: forEachSyntax.InKeyword.Span, rewrittenBody: rewrittenBodyBlock, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: false); BoundStatement result; MethodSymbol disposeMethod; if (enumeratorInfo.NeedsDisposeMethod && Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, forEachSyntax, _diagnostics, out disposeMethod)) { Binder.ReportDiagnosticsIfObsolete(_diagnostics, disposeMethod, forEachSyntax, hasBaseReceiver: false, containingMember: _factory.CurrentMethod, containingType: _factory.CurrentType, location: enumeratorInfo.Location); BoundBlock finallyBlockOpt; var idisposableTypeSymbol = disposeMethod.ContainingType; var conversions = new TypeConversions(_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: _compilation.GetSpecialType(SpecialType.System_Object), @checked: false), right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: _compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: disposeCall, rewrittenAlternativeOpt: null, hasErrors: false); } finallyBlockOpt = new BoundBlock(forEachSyntax, locals: ImmutableArray<LocalSymbol>.Empty, localFunctions: ImmutableArray<LocalFunctionSymbol>.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: _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), localFunctions: ImmutableArray<LocalFunctionSymbol>.Empty, 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, localFunctions: ImmutableArray<LocalFunctionSymbol>.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), localFunctions: ImmutableArray<LocalFunctionSymbol>.Empty, 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), localFunctions: ImmutableArray<LocalFunctionSymbol>.Empty, statements: ImmutableArray.Create<BoundStatement>(enumeratorVarDecl, whileLoop)); } AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private BoundStatement RewriteUsingStatementTryFinally(SyntaxNode syntax, BoundBlock tryBlock, BoundLocal local, SyntaxToken awaitKeywordOpt, AwaitableInfo awaitOpt, MethodSymbol methodOpt) { // 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. // // In the case of using-await statement, we'll use "IAsyncDisposable" instead of "IDisposable", "await DisposeAsync()" instead of "Dispose()" // // 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. Debug.Assert((awaitKeywordOpt == default) == (awaitOpt == default(AwaitableInfo))); BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } BoundExpression disposeCall = GenerateDisposeCall(syntax, disposedExpression, methodOpt, awaitOpt, awaitKeywordOpt); // local.Dispose(); or await variant 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(); or await variant finallyStatement = disposeStatement; } else { // if (local != null) local.Dispose(); // or // if (local.HasValue) local.GetValueOrDefault().Dispose(); // or // await variants finallyStatement = RewriteIfStatement( syntax: syntax, rewrittenCondition: ifCondition, rewrittenConsequence: disposeStatement, rewrittenAlternativeOpt: null, hasErrors: false); } // try { ... } finally { if (local != null) local.Dispose(); } // or // nullable or await variants BoundStatement tryFinally = new BoundTryStatement( syntax: syntax, tryBlock: tryBlock, catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement)); return(tryFinally); }
/// <summary> /// Wrap a given expression e into a block as either { e; } or { return e; } /// Shared between lambda and expression-bodied method binding. /// </summary> internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableArray<LocalSymbol> locals, RefKind refKind, BoundExpression expression, ExpressionSyntax expressionSyntax, DiagnosticBag diagnostics) { RefKind returnRefKind; var returnType = GetCurrentReturnType(out returnRefKind); var syntax = expressionSyntax ?? expression.Syntax; BoundStatement statement; if (IsInAsyncMethod() && refKind != RefKind.None) { // This can happen if we are binding an async anonymous method to a delegate type. Error(diagnostics, ErrorCode.ERR_MustNotHaveRefReturn, syntax); statement = new BoundReturnStatement(syntax, refKind, expression) { WasCompilerGenerated = true }; } else if ((object)returnType != null) { if ((refKind != RefKind.None) != (returnRefKind != RefKind.None)) { var errorCode = refKind != RefKind.None ? ErrorCode.ERR_MustNotHaveRefReturn : ErrorCode.ERR_MustHaveRefReturn; Error(diagnostics, errorCode, syntax); statement = new BoundReturnStatement(syntax, RefKind.None, expression) { WasCompilerGenerated = true }; } else if (returnType.SpecialType == SpecialType.System_Void || IsTaskReturningAsyncMethod()) { // If the return type is void then the expression is required to be a legal // statement expression. Debug.Assert(expressionSyntax != null || !IsValidStatementExpression(expression.Syntax, expression)); bool errors = false; if (expressionSyntax == null || !IsValidStatementExpression(expressionSyntax, expression)) { Error(diagnostics, ErrorCode.ERR_IllegalStatement, syntax); errors = true; } // Don't mark compiler generated so that the rewriter generates sequence points var expressionStatement = new BoundExpressionStatement(syntax, expression, errors); CheckForUnobservedAwaitable(expression, diagnostics); statement = expressionStatement; } else { expression = CreateReturnConversion(syntax, diagnostics, expression, refKind, returnType); statement = new BoundReturnStatement(syntax, returnRefKind, expression) { WasCompilerGenerated = true }; } } else if (expression.Type?.SpecialType == SpecialType.System_Void) { statement = new BoundExpressionStatement(syntax, expression) { WasCompilerGenerated = true }; } else { statement = new BoundReturnStatement(syntax, refKind, expression) { WasCompilerGenerated = true }; } // Need to attach the tree for when we generate sequence points. return new BoundBlock(node, locals, ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create(statement)) { WasCompilerGenerated = node.Kind() != SyntaxKind.ArrowExpressionClause }; }
// NOTE: can return null if the method has no body. internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList<Imports> debugImports) { debugImports = null; BoundStatement constructorInitializer = null; BoundBlock body; var compilation = method.DeclaringCompilation; var sourceMethod = method as SourceMethodSymbol; if ((object)sourceMethod != null) { if (sourceMethod.IsExtern) { if (sourceMethod.BlockSyntax == null) { // Generate warnings only if we are not generating ERR_ExternHasBody error GenerateExternalMethodWarnings(sourceMethod, diagnostics); } return null; } else if (sourceMethod.IsParameterlessValueTypeConstructor(requireSynthesized: true)) { // No body for default struct constructor. return null; } var blockSyntax = sourceMethod.BlockSyntax; if (blockSyntax != null) { var factory = compilation.GetBinderFactory(sourceMethod.SyntaxTree); var inMethodBinder = factory.GetBinder(blockSyntax); var binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder); body = binder.BindBlock(blockSyntax, diagnostics); if (generateDebugInfo) { debugImports = binder.ImportsList; } if (inMethodBinder.IsDirectlyInIterator) { foreach (var parameter in method.Parameters) { if (parameter.RefKind != RefKind.None) { diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, parameter.Locations[0]); } else if (parameter.Type.IsUnsafe()) { diagnostics.Add(ErrorCode.ERR_UnsafeIteratorArgType, parameter.Locations[0]); } } if (sourceMethod.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade { diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceMethod.Locations[0]); } if (sourceMethod.IsVararg) { // error CS1636: __arglist is not allowed in the parameter list of iterators diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceMethod.Locations[0]); } } } else // for [if (blockSyntax != null)] { var property = sourceMethod.AssociatedSymbol as SourcePropertySymbol; if ((object)property != null && property.IsAutoProperty) { return MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod); } if (sourceMethod.IsPrimaryCtor) { body = null; } else { return null; } } } else { // synthesized methods should return their bound bodies body = null; } // delegates have constructors but not constructor initializers if (method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType()) { var initializerInvocation = BindConstructorInitializer(method, diagnostics, compilation); if (initializerInvocation != null) { constructorInitializer = new BoundExpressionStatement(initializerInvocation.Syntax, initializerInvocation) { WasCompilerGenerated = true }; Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer."); } } var statements = ArrayBuilder<BoundStatement>.GetInstance(); if (constructorInitializer != null) { statements.Add(constructorInitializer); } if ((object)sourceMethod != null && sourceMethod.IsPrimaryCtor && (object)((SourceMemberContainerTypeSymbol)sourceMethod.ContainingType).PrimaryCtor == (object)sourceMethod) { Debug.Assert(method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType()); Debug.Assert(body == null); if (sourceMethod.ParameterCount > 0) { var factory = new SyntheticBoundNodeFactory(sourceMethod, sourceMethod.SyntaxNode, compilationState, diagnostics); factory.CurrentMethod = sourceMethod; foreach (var parameter in sourceMethod.Parameters) { FieldSymbol field = parameter.PrimaryConstructorParameterBackingField; if ((object)field != null) { statements.Add(factory.Assignment(factory.Field(factory.This(), field), factory.Parameter(parameter))); } } } } if (body != null) { statements.Add(body); } CSharpSyntaxNode syntax = body != null ? body.Syntax : method.GetNonNullSyntaxNode(); BoundBlock block; if (statements.Count == 1 && statements[0].Kind == ((body == null) ? BoundKind.Block : body.Kind)) { // most common case - we just have a single block for the body. block = (BoundBlock)statements[0]; statements.Free(); } else { block = new BoundBlock(syntax, default(ImmutableArray<LocalSymbol>), statements.ToImmutableAndFree()) { WasCompilerGenerated = true }; } return method.MethodKind == MethodKind.Destructor ? MethodBodySynthesizer.ConstructDestructorBody(syntax, method, block) : block; }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { if (node.Expression.Kind == BoundKind.AwaitExpression) { var awaitExpression = VisitAwaitExpression((BoundAwaitExpression)node.Expression, resultsDiscarded: true); return node.Update(awaitExpression); } return base.VisitExpressionStatement(node); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { // expressions cannot contain labels, branches or yields. return null; }
internal static ImmutableArray <BoundStatement> ConstructScriptConstructorBody( BoundStatement loweredBody, MethodSymbol constructor, SynthesizedSubmissionFields previousSubmissionFields, CSharpCompilation compilation ) { // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance. // // Unlike regular field initializers, initializers of global script variables can access "this" instance. // If the base class had a constructor that initializes its state a global variable would access partially initialized object. // For this reason Script class must always derive directly from a class that has no state (System.Object). SyntaxNode syntax = loweredBody.Syntax; // base constructor call: Debug.Assert( (object)constructor.ContainingType.BaseTypeNoUseSiteDiagnostics == null || constructor.ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object ); var objectType = constructor.ContainingAssembly.GetSpecialType( SpecialType.System_Object ); BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true }; BoundStatement baseConstructorCall = new BoundExpressionStatement( syntax, new BoundCall( syntax, receiverOpt: receiver, method: objectType.InstanceConstructors[0], arguments: ImmutableArray <BoundExpression> .Empty, argumentNamesOpt: ImmutableArray <string> .Empty, argumentRefKindsOpt: ImmutableArray <RefKind> .Empty, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: ImmutableArray <int> .Empty, defaultArguments: BitVector.Empty, resultKind: LookupResultKind.Viable, type: objectType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; var statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.Add(baseConstructorCall); if (constructor.IsSubmissionConstructor) { // submission initialization: MakeSubmissionInitialization( statements, syntax, constructor, previousSubmissionFields, compilation ); } statements.Add(loweredBody); return(statements.ToImmutableAndFree()); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { BoundSpillSequence2 ss = null; BoundExpression expr; if (node.Expression.Kind == BoundKind.AwaitExpression) { // await expression with result discarded var awaitExpression = (BoundAwaitExpression)node.Expression; var expression = VisitExpression(ref ss, awaitExpression.Expression); expr = awaitExpression.Update(expression, awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type); } else { expr = VisitExpression(ref ss, node.Expression); } Debug.Assert(expr != null); Debug.Assert(ss == null || ss.Value == null); BoundStatement replacement = UpdateStatement(ss, node.Update(expr)); return replacement; }
/// <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<LocalFunctionSymbol>.Empty, ImmutableArray.Create( MakeInitialLockSequencePoint(boundLockTempInit, lockSyntax), 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<LocalFunctionSymbol>.Empty, ImmutableArray.Create( MakeInitialLockSequencePoint(boundLockTempInit, lockSyntax), enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray<BoundCatchBlock>.Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))); } }
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; }
public BoundExpressionStatement BindExpressionStatement(CSharpSyntaxNode node, ExpressionSyntax syntax, bool allowsAnyExpression, DiagnosticBag diagnostics) { BoundExpressionStatement expressionStatement; var expression = BindValue(syntax, diagnostics, BindValueKind.RValue); if (!allowsAnyExpression && !IsValidStatementExpression(syntax, expression)) { if (!node.HasErrors) { Error(diagnostics, ErrorCode.ERR_IllegalStatement, syntax); } expressionStatement = new BoundExpressionStatement(node, expression, hasErrors: true); } else { expressionStatement = new BoundExpressionStatement(node, expression); } CheckForUnobservedAwaitable(expressionStatement, diagnostics); return expressionStatement; }
private static BoundStatement RewriteFieldInitializer(BoundFieldEqualsValue fieldInit) { var syntax = fieldInit.Syntax; syntax = (syntax as EqualsValueClauseSyntax)?.Value ?? syntax; //we want the attached sequence point to indicate the value node var boundReceiver = fieldInit.Field.IsStatic ? null : new BoundThisReference(syntax, fieldInit.Field.ContainingType); #if XSHARP var initValue = fieldInit.Value; // a generated initial value for VO NULL_STRING initialization // should not overwrite a value set in a child class // not that we recommend that <g> bool wasGenerated = fieldInit.WasCompilerGenerated; if (!wasGenerated) { var xNode = initValue.Syntax as LiteralExpressionSyntax; if (xNode != null && xNode.XGenerated) { wasGenerated = true; } } if (wasGenerated && fieldInit.Field.Type.IsStringType() && boundReceiver != null && fieldInit.Field.DeclaringCompilation.Options.HasOption(CompilerOption.NullStrings, boundReceiver.Syntax)) { var fldaccess = new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null) { WasCompilerGenerated = true }; initValue = new BoundNullCoalescingOperator(syntax, fldaccess, initValue, Conversion.Identity, fieldInit.Field.Type) { WasCompilerGenerated = true }; } BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null), initValue, fieldInit.Field.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = fieldInit.WasCompilerGenerated }; #else BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, fieldInit.Field, constantValueOpt: null), fieldInit.Value, fieldInit.Field.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = !fieldInit.Locals.IsEmpty || fieldInit.WasCompilerGenerated }; #endif if (!fieldInit.Locals.IsEmpty) { boundStatement = new BoundBlock(syntax, fieldInit.Locals, ImmutableArray.Create(boundStatement)) { WasCompilerGenerated = fieldInit.WasCompilerGenerated }; } Debug.Assert(LocalRewriter.IsFieldOrPropertyInitializer(boundStatement)); return(boundStatement); }
private static BoundInitializer BindGlobalStatement(Binder binder, StatementSyntax statementNode, DiagnosticBag diagnostics, bool isLast) { BoundStatement boundStatement = binder.BindStatement(statementNode, diagnostics); // the result of the last global expression is assigned to the result storage for submission result: if (binder.Compilation.IsSubmission && isLast && boundStatement.Kind == BoundKind.ExpressionStatement && !boundStatement.HasAnyErrors) { var submissionReturnType = binder.Compilation.GetSubmissionReturnType(); // insert an implicit conversion for the submission return type (if needed): var expression = ((BoundExpressionStatement)boundStatement).Expression; if ((object)expression.Type == null || expression.Type.SpecialType != SpecialType.System_Void) { expression = binder.GenerateConversionForAssignment(submissionReturnType, expression, diagnostics); boundStatement = new BoundExpressionStatement(boundStatement.Syntax, expression, expression.HasErrors); } } return new BoundGlobalStatementInitializer(statementNode, boundStatement); }
public override BoundStatement InstrumentExpressionStatement(BoundExpressionStatement original, BoundStatement rewritten) { return(AddDynamicAnalysis(original, base.InstrumentExpressionStatement(original, rewritten))); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { // ref assignments might be translated away (into nothing). If so just // return no statement. The enclosing statement list will just omit it. BoundExpression expression = (BoundExpression)this.Visit(node.Expression); return (expression == null) ? null : node.Update(expression); }
/// <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 = 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, 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 = this.compilation.GetSpecialType(SpecialType.System_Boolean); BoundAssignmentOperator assignmentToLockTakenTemp; BoundLocal boundLockTakenTemp = this.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( MakeInitialLockSequencePoint(boundLockTempInit, lockSyntax), 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( MakeInitialLockSequencePoint(boundLockTempInit, lockSyntax), enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } }
/// <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, out assignmentToLockTemp, kind: SynthesizedLocalKind.Lock); 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), kind: SynthesizedLocalKind.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, node.Locals.Concat(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, node.Locals.Add(boundLockTemp.LocalSymbol), ImmutableArray.Create<BoundStatement>( boundLockTempInit, enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray<BoundCatchBlock>.Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))); } }
/// <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 && Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, forEachSyntax, _diagnostics, out disposeMethod)) { Binder.ReportDiagnosticsIfObsolete(_diagnostics, disposeMethod, forEachSyntax, hasBaseReceiver: false, containingMember: _factory.CurrentMethod, containingType: _factory.CurrentType, location: enumeratorInfo.Location); BoundBlock finallyBlockOpt; var idisposableTypeSymbol = disposeMethod.ContainingType; var conversions = new TypeConversions(_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: _compilation.GetSpecialType(SpecialType.System_Object), @checked: false), right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: _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: _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); }
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()); }
// NOTE: can return null if the method has no body. internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList <Imports> debugImports) { debugImports = null; BoundStatement constructorInitializer = null; BoundBlock body; var compilation = method.DeclaringCompilation; var sourceMethod = method as SourceMethodSymbol; if ((object)sourceMethod != null) { if (sourceMethod.IsExtern) { if (sourceMethod.BlockSyntax == null) { // Generate warnings only if we are not generating ERR_ExternHasBody error GenerateExternalMethodWarnings(sourceMethod, diagnostics); } return(null); } else if (sourceMethod.IsParameterlessValueTypeConstructor(requireSynthesized: true)) { // No body for default struct constructor. return(null); } var blockSyntax = sourceMethod.BlockSyntax; if (blockSyntax != null) { var factory = compilation.GetBinderFactory(sourceMethod.SyntaxTree); var inMethodBinder = factory.GetBinder(blockSyntax); var binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder); body = binder.BindBlock(blockSyntax, diagnostics); if (generateDebugInfo) { debugImports = binder.ImportsList; } if (inMethodBinder.IsDirectlyInIterator) { foreach (var parameter in method.Parameters) { if (parameter.RefKind != RefKind.None) { diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, parameter.Locations[0]); } else if (parameter.Type.IsUnsafe()) { diagnostics.Add(ErrorCode.ERR_UnsafeIteratorArgType, parameter.Locations[0]); } } if (sourceMethod.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade { diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceMethod.Locations[0]); } if (sourceMethod.IsVararg) { // error CS1636: __arglist is not allowed in the parameter list of iterators diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceMethod.Locations[0]); } } } else // for [if (blockSyntax != null)] { var property = sourceMethod.AssociatedSymbol as SourcePropertySymbol; if ((object)property != null && property.IsAutoProperty) { return(MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod)); } if (sourceMethod.IsPrimaryCtor) { body = null; } else { return(null); } } } else { // synthesized methods should return their bound bodies body = null; } // delegates have constructors but not constructor initializers if (method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType()) { var initializerInvocation = BindConstructorInitializer(method, diagnostics, compilation); if (initializerInvocation != null) { constructorInitializer = new BoundExpressionStatement(initializerInvocation.Syntax, initializerInvocation) { WasCompilerGenerated = true }; Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer."); } } var statements = ArrayBuilder <BoundStatement> .GetInstance(); if (constructorInitializer != null) { statements.Add(constructorInitializer); } if ((object)sourceMethod != null && sourceMethod.IsPrimaryCtor && (object)((SourceMemberContainerTypeSymbol)sourceMethod.ContainingType).PrimaryCtor == (object)sourceMethod) { Debug.Assert(method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType()); Debug.Assert(body == null); if (sourceMethod.ParameterCount > 0) { var factory = new SyntheticBoundNodeFactory(sourceMethod, sourceMethod.SyntaxNode, compilationState, diagnostics); factory.CurrentMethod = sourceMethod; foreach (var parameter in sourceMethod.Parameters) { FieldSymbol field = parameter.PrimaryConstructorParameterBackingField; if ((object)field != null) { statements.Add(factory.Assignment(factory.Field(factory.This(), field), factory.Parameter(parameter))); } } } } if (body != null) { statements.Add(body); } CSharpSyntaxNode syntax = body != null ? body.Syntax : method.GetNonNullSyntaxNode(); BoundBlock block; if (statements.Count == 1 && statements[0].Kind == ((body == null) ? BoundKind.Block : body.Kind)) { // most common case - we just have a single block for the body. block = (BoundBlock)statements[0]; statements.Free(); } else { block = new BoundBlock(syntax, default(ImmutableArray <LocalSymbol>), statements.ToImmutableAndFree()) { WasCompilerGenerated = true }; } return(method.MethodKind == MethodKind.Destructor ? MethodBodySynthesizer.ConstructDestructorBody(syntax, method, block) : block); }
/// <summary> /// Lower "using (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator. return tryBlock; } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversion( expressionSyntax, rewrittenExpression, node.IDisposableConversion.Kind, this.compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); } else { // ResourceType temp = expr; boundTemp = this.factory.StoreToTemp(rewrittenExpression, out tempAssignment, kind: SynthesizedLocalKind.Using); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { expressionStatement = AddSequencePoint(usingSyntax, expressionStatement); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return new BoundBlock( syntax: usingSyntax, locals: node.Locals.Add(boundTemp.LocalSymbol), statements: ImmutableArray.Create<BoundStatement>(expressionStatement, tryFinally)); }
/// <summary> /// Prepares most of the parts for MoveNextAsync() and DisposeAsync() methods. /// </summary> private void GetPartsForStartingMachine(TypeSymbol returnType, out BoundExpressionStatement callReset, out LocalSymbol instSymbol, out BoundStatement instAssignment, out BoundExpressionStatement startCall, out MethodSymbol promise_get_Version) { // Produce the following parts: // - _promiseOfValueOrEnd.Reset(); // - var inst = this; // - _builder.Start(ref inst); // - _valueOrEndPromise.Version // _promiseOfValueOrEnd.Reset(); BoundFieldAccess promiseField = F.InstanceField(_promiseOfValueOrEndField); var resetMethod = (MethodSymbol)F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__Reset, isOptional: true) .SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol); callReset = F.ExpressionStatement(F.Call(promiseField, resetMethod)); // _builder.Start(ref inst); Debug.Assert(!_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints); MethodSymbol startMethod = _asyncMethodBuilderMemberCollection.Start.Construct(this.stateMachineType); instSymbol = F.SynthesizedLocal(this.stateMachineType); // var inst = this; var instLocal = F.Local(instSymbol); instAssignment = F.Assignment(instLocal, F.This()); // _builder.Start(ref inst); startCall = F.ExpressionStatement( F.Call( F.InstanceField(_builderField), startMethod, ImmutableArray.Create <BoundExpression>(instLocal))); // _valueOrEndPromise.Version promise_get_Version = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__get_Version) .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol); }