public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) { if (IsInside) { _variablesDeclared.Add(node.LocalSymbol); } return base.VisitLocalDeclaration(node); }
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); }
private BoundStatement InitializeFixedStatementStringLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol stringTemp, out LocalSymbol localToClear) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString); // NOTE: we pin the string, not the pointer. Debug.Assert(stringTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr); var convertedStringTemp = factory.Convert( localType, factory.Local(stringTemp), fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray <BoundExpression> .Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); localToClear = stringTemp; return(factory.Block(stringTempInit, localInit, conditionalAdd)); }
/// <summary> /// Lower "using (ResourceType resource = expression) statement" to a try-finally block. /// </summary> /// <remarks> /// Assumes that the local symbol will be declared (i.e. in the LocalsOpt array) of an enclosing block. /// Assumes that using statements with multiple locals have already been split up into multiple using statements. /// </remarks> private BoundBlock RewriteDeclarationUsingStatement(SyntaxNode usingSyntax, BoundLocalDeclaration localDeclaration, BoundBlock tryBlock, Conversion idisposableConversion) { SyntaxNode declarationSyntax = localDeclaration.Syntax; LocalSymbol localSymbol = localDeclaration.LocalSymbol; TypeSymbol localType = localSymbol.Type; Debug.Assert((object)localType != null); //otherwise, there wouldn't be a conversion to IDisposable BoundLocal boundLocal = new BoundLocal(declarationSyntax, localSymbol, localDeclaration.InitializerOpt.ConstantValue, localType); BoundStatement rewrittenDeclaration = (BoundStatement)Visit(localDeclaration); // If we know that the expression is null, then we know that the null check in the finally block // will fail, and the Dispose call will never happen. That is, the finally block will have no effect. // Consequently, we can simply skip the whole try-finally construct and just create a block containing // the new declaration. if (boundLocal.ConstantValue == ConstantValue.Null) { //localSymbol will be declared by an enclosing block return(BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryBlock)); } if (localType.IsDynamic()) { BoundExpression tempInit = MakeConversionNode( declarationSyntax, boundLocal, idisposableConversion, _compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(tempInit, out tempAssignment, kind: SynthesizedLocalKind.Using); BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); return(new BoundBlock( syntax: usingSyntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), //localSymbol will be declared by an enclosing block statements: ImmutableArray.Create <BoundStatement>( rewrittenDeclaration, new BoundExpressionStatement(declarationSyntax, tempAssignment), tryFinally))); } else { BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundLocal); // localSymbol will be declared by an enclosing block return(BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryFinally)); } }
private BoundStatement InstrumentLocalDeclarationIfNecessary(BoundLocalDeclaration originalOpt, LocalSymbol localSymbol, BoundStatement rewrittenLocalDeclaration) { // Add sequence points, if necessary. if (this.Instrument && originalOpt?.WasCompilerGenerated == false && !localSymbol.IsConst && (originalOpt.Syntax.Kind() == SyntaxKind.VariableDeclarator || (originalOpt.Syntax.Kind() == SyntaxKind.LocalDeclarationStatement && ((LocalDeclarationStatementSyntax)originalOpt.Syntax).Declaration.Variables.Count == 1))) { rewrittenLocalDeclaration = _instrumenter.InstrumentLocalInitialization(originalOpt, rewrittenLocalDeclaration); } return rewrittenLocalDeclaration; }
private BoundStatement InstrumentLocalDeclarationIfNecessary(BoundLocalDeclaration originalOpt, LocalSymbol localSymbol, BoundStatement rewrittenLocalDeclaration) { // Add sequence points, if necessary. if (this.Instrument && originalOpt?.WasCompilerGenerated == false && !localSymbol.IsConst && (originalOpt.Syntax.Kind() == SyntaxKind.VariableDeclarator || (originalOpt.Syntax.Kind() == SyntaxKind.LocalDeclarationStatement && ((LocalDeclarationStatementSyntax)originalOpt.Syntax).Declaration.Variables.Count == 1))) { rewrittenLocalDeclaration = _instrumenter.InstrumentLocalInitialization(originalOpt, rewrittenLocalDeclaration); } return(rewrittenLocalDeclaration); }
public override BoundNode VisitFixedStatement(BoundFixedStatement node) { ImmutableArray <BoundLocalDeclaration> localDecls = node.Declarations.LocalDeclarations; int numFixedLocals = localDecls.Length; var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(node.Locals.Length); localBuilder.AddRange(node.Locals); var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(numFixedLocals + 1 + 1); //+1 for body, +1 for hidden seq point var cleanup = new BoundStatement[numFixedLocals]; for (int i = 0; i < numFixedLocals; i++) { BoundLocalDeclaration localDecl = localDecls[i]; LocalSymbol pinnedTemp; statementBuilder.Add( InitializeFixedStatementLocal(localDecl, _factory, out pinnedTemp) ); localBuilder.Add(pinnedTemp); // NOTE: Dev10 nulls out the locals in declaration order (as opposed to "popping" them in reverse order). if (pinnedTemp.RefKind == RefKind.None) { // temp = null; cleanup[i] = _factory.Assignment( _factory.Local(pinnedTemp), _factory.Null(pinnedTemp.Type) ); } else { Debug.Assert(!pinnedTemp.Type.IsManagedTypeNoUseSiteDiagnostics); // temp = ref *default(T*); cleanup[i] = _factory.Assignment( _factory.Local(pinnedTemp), new BoundPointerIndirectionOperator( _factory.Syntax, _factory.Default(new PointerTypeSymbol(pinnedTemp.TypeWithAnnotations)), pinnedTemp.Type ), isRef: true ); } } BoundStatement?rewrittenBody = VisitStatement(node.Body); Debug.Assert(rewrittenBody is { });
public virtual BoundStatement InstrumentLocalInitialization( BoundLocalDeclaration original, BoundStatement rewritten ) { Debug.Assert( original.Syntax.Kind() == SyntaxKind.VariableDeclarator || ( original.Syntax.Kind() == SyntaxKind.LocalDeclarationStatement && ( (LocalDeclarationStatementSyntax)original.Syntax ).Declaration.Variables.Count == 1 ) ); return(InstrumentStatement(original, rewritten)); }
private BoundStatement BindDeclarationStatement(LocalDeclarationStatementSyntax node, DiagnosticBag diagnostics) { var typeSyntax = node.Declaration.Type; bool isConst = node.IsConst; bool isVar; AliasSymbol alias; TypeSymbol declType = BindVariableType(node, diagnostics, typeSyntax, ref isConst, isVar: out isVar, alias: out alias); // UNDONE: "possible expression" feature for IDE LocalDeclarationKind kind = LocalDeclarationKind.RegularVariable; if (isConst) { kind = LocalDeclarationKind.Constant; } var variableList = node.Declaration.Variables; int variableCount = variableList.Count; if (variableCount == 1) { return BindVariableDeclaration(kind, isVar, variableList[0], typeSyntax, declType, alias, diagnostics, node); } else { BoundLocalDeclaration[] boundDeclarations = new BoundLocalDeclaration[variableCount]; int i = 0; foreach (var variableDeclaratorSyntax in variableList) { boundDeclarations[i++] = BindVariableDeclaration(kind, isVar, variableDeclaratorSyntax, typeSyntax, declType, alias, diagnostics); } return new BoundMultipleLocalDeclarations(node, boundDeclarations.AsImmutableOrNull()); } }
public override BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { return(AddDynamicAnalysis(original, base.InstrumentLocalInitialization(original, rewritten))); }
internal BoundStatement BindForOrUsingOrFixedDeclarations(VariableDeclarationSyntax nodeOpt, LocalDeclarationKind localKind, DiagnosticBag diagnostics, out ImmutableArray<BoundLocalDeclaration> declarations) { if (nodeOpt == null) { declarations = ImmutableArray<BoundLocalDeclaration>.Empty; return null; } var typeSyntax = nodeOpt.Type; AliasSymbol alias; bool isVar; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); Debug.Assert((object)declType != null || isVar); var variables = nodeOpt.Variables; int count = variables.Count; Debug.Assert(count > 0); if (isVar && count > 1) { // There are a number of ways in which a var decl can be illegal, but in these // cases we should report an error and then keep right on going with the inference. Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, nodeOpt); } var declarationArray = new BoundLocalDeclaration[count]; for (int i = 0; i < count; i++) { var variableDeclarator = variables[i]; var declaration = BindVariableDeclaration(localKind, isVar, variableDeclarator, typeSyntax, declType, alias, diagnostics); declarationArray[i] = declaration; } declarations = declarationArray.AsImmutableOrNull(); return (count == 1) ? (BoundStatement)declarations[0] : new BoundMultipleLocalDeclarations(nodeOpt, declarations); }
public override BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { return AddSequencePoint(original.Syntax.Kind() == SyntaxKind.VariableDeclarator ? (VariableDeclaratorSyntax)original.Syntax : ((LocalDeclarationStatementSyntax)original.Syntax).Declaration.Variables.First(), base.InstrumentLocalInitialization(original, rewritten)); }
public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) { CheckDeclared(node.LocalSymbol); base.VisitLocalDeclaration(node); return(null); }
private BoundStatement InitializeFixedStatementArrayLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayType.IsSZArray) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit); }
public override BoundNode? VisitLocalDeclaration(BoundLocalDeclaration node) { return RewriteLocalDeclaration(node, node.Syntax, node.LocalSymbol, VisitExpression(node.InitializerOpt), node.HasErrors); }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = arr){ ... } == becomes ===> /// /// pinned int[] pinnedTemp = arr; // pinning managed ref /// int* ptr = pinnedTemp != null && pinnedTemp.Length != 0 /// (int*)&pinnedTemp[0]: // unsafe cast to unmanaged ptr /// 0; /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementArrayLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; pinnedTemp = factory.SynthesizedLocal(initializerType, isPinned: true); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)pinnedTemp.Type; TypeWithAnnotations arrayElementType = arrayType.ElementTypeWithAnnotations; // NOTE: we pin the array, not the pointer. Debug.Assert(pinnedTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); //(pinnedTemp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(pinnedTemp), initializerExpr); //(pinnedTemp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayType.IsSZArray) { lengthCall = factory.ArrayLength(factory.Local(pinnedTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(pinnedTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(factory.Local(pinnedTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(pinnedTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of the pointer. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, false, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit)); }
private BoundStatement InitializeFixedStatementArrayLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayType.IsSZArray) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit)); }
private static void DeclareLocal(ref ArrayBuilder<LocalSymbol> locals, BoundLocalDeclaration localDecl) { if (locals == null) { locals = ArrayBuilder<LocalSymbol>.GetInstance(); } locals.Add(localDecl.LocalSymbol); }
private BoundStatement InitializeFixedStatementLocal( BoundLocalDeclaration localDecl, SyntheticBoundNodeFactory factory, out LocalSymbol temp, out LocalSymbol localToClear) { BoundExpression initializer = localDecl.InitializerOpt; Debug.Assert(!ReferenceEquals(initializer, null)); LocalSymbol localSymbol = localDecl.LocalSymbol; if (initializer.Kind == BoundKind.FixedLocalCollectionInitializer) { var fixedInitializer = (BoundFixedLocalCollectionInitializer)initializer; if (fixedInitializer.Expression.Type.SpecialType == SpecialType.System_String) { return InitializeFixedStatementStringLocal(localSymbol, fixedInitializer, factory, out temp, out localToClear); } else { Debug.Assert(fixedInitializer.Expression.Type.IsArray()); localToClear = localSymbol; return InitializeFixedStatementArrayLocal(localSymbol, fixedInitializer, factory, out temp); } } else { temp = null; localToClear = localSymbol; return RewriteLocalDeclaration(localDecl.Syntax, localSymbol, VisitExpression(initializer)); } }
public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) { return RewriteLocalDeclaration(node, node.Syntax, node.LocalSymbol, VisitExpression(node.InitializerOpt), node.HasErrors); }
private BoundStatement BindDeclarationStatement(LocalDeclarationStatementSyntax node, DiagnosticBag diagnostics) { var typeSyntax = node.Declaration.Type; bool isConst = node.IsConst; bool isFixed = node.IsFixed; // If the type is "var" then suppress errors when binding it. "var" might be a legal type // or it might not; if it is not then we do not want to report an error. If it is, then // we want to treat the declaration as an explicitly typed declaration. bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); Debug.Assert((object)declType != null || isVar); if (isVar) { // There are a number of ways in which a var decl can be illegal, but in these // cases we should report an error and then keep right on going with the inference. if (isFixed) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, node); } if (isConst) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableCannotBeConst, node); // Keep processing it as a non-const local. isConst = false; } // In the dev10 compiler the error recovery semantics for the illegal case // "var x = 10, y = 123.4;" are somewhat undesirable. // // First off, this is an error because a straw poll of language designers and // users showed that there was no consensus on whether the above should mean // "double x = 10, y = 123.4;", taking the best type available and substituting // that for "var", or treating it as "var x = 10; var y = 123.4;" -- since there // was no consensus we decided to simply make it illegal. // // In dev10 for error recovery in the IDE we do an odd thing -- we simply take // the type of the first variable and use it. So that is "int x = 10, y = 123.4;". // // This seems less than ideal. In the error recovery scenario it probably makes // more sense to treat that as "var x = 10; var y = 123.4;" and do each inference // separately. if (node.Declaration.Variables.Count > 1 && !node.HasErrors) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, node); } } else { // In the native compiler when given a situation like // // D[] x; // // where D is a static type we report both that D cannot be an element type // of an array, and that D[] is not a valid type for a local variable. // This seems silly; the first error is entirely sufficient. We no longer // produce additional errors for local variables of arrays of static types. if (declType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, declType); } if (isFixed && !(declType is PointerTypeSymbol)) { Error(diagnostics, ErrorCode.ERR_BadFixedInitType, node); } if (isConst && !declType.CanBeConst()) { Error(diagnostics, ErrorCode.ERR_BadConstType, typeSyntax, declType); // Keep processing it as a non-const local. isConst = false; } } // UNDONE: "possible expression" feature for IDE LocalDeclarationKind kind = LocalDeclarationKind.Variable; if (isFixed) { kind = LocalDeclarationKind.Fixed; } if (isConst) { kind = LocalDeclarationKind.Constant; } var variableList = node.Declaration.Variables; int variableCount = variableList.Count; if (variableCount == 1) { return BindVariableDeclaration(kind, isVar, variableList[0], typeSyntax, declType, alias, diagnostics, node); } else { BoundLocalDeclaration[] boundDeclarations = new BoundLocalDeclaration[variableCount]; int i = 0; foreach (var variableDeclaratorSyntax in variableList) { boundDeclarations[i++] = BindVariableDeclaration(kind, isVar, variableDeclaratorSyntax, typeSyntax, declType, alias, diagnostics); } return new BoundMultipleLocalDeclarations(node, boundDeclarations.AsImmutableOrNull()); } }
public virtual BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { Debug.Assert(original.Syntax.Kind() == SyntaxKind.VariableDeclarator || (original.Syntax.Kind() == SyntaxKind.LocalDeclarationStatement && ((LocalDeclarationStatementSyntax)original.Syntax).Declaration.Variables.Count == 1)); return InstrumentStatement(original, rewritten); }
public override BoundNode VisitFixedStatement(BoundFixedStatement node) { ImmutableArray <BoundLocalDeclaration> localDecls = node.Declarations.LocalDeclarations; int numFixedLocals = localDecls.Length; var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(node.Locals.Length); localBuilder.AddRange(node.Locals); var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(numFixedLocals + 1 + 1); //+1 for body, +1 for hidden seq point var cleanup = new BoundStatement[numFixedLocals]; for (int i = 0; i < numFixedLocals; i++) { BoundLocalDeclaration localDecl = localDecls[i]; LocalSymbol pinnedTemp; statementBuilder.Add(InitializeFixedStatementLocal(localDecl, _factory, out pinnedTemp)); localBuilder.Add(pinnedTemp); // NOTE: Dev10 nulls out the locals in declaration order (as opposed to "popping" them in reverse order). if (pinnedTemp.RefKind == RefKind.None) { // temp = null; cleanup[i] = _factory.Assignment(_factory.Local(pinnedTemp), _factory.Null(pinnedTemp.Type)); } else { Debug.Assert(!pinnedTemp.Type.IsManagedType); // temp = ref *default(T*); cleanup[i] = _factory.Assignment(_factory.Local(pinnedTemp), new BoundPointerIndirectionOperator( _factory.Syntax, _factory.Default(new PointerTypeSymbol(pinnedTemp.TypeWithAnnotations)), pinnedTemp.Type), isRef: true); } } BoundStatement rewrittenBody = VisitStatement(node.Body); statementBuilder.Add(rewrittenBody); statementBuilder.Add(_factory.HiddenSequencePoint()); Debug.Assert(statementBuilder.Count == numFixedLocals + 1 + 1); // In principle, the cleanup code (i.e. nulling out the pinned variables) is always // in a finally block. However, we can optimize finally away (keeping the cleanup // code) in cases where both of the following are true: // 1) there are no branches out of the fixed statement; and // 2) the fixed statement is not in a try block (syntactic or synthesized). if (IsInTryBlock(node) || HasGotoOut(rewrittenBody)) { return(_factory.Block( localBuilder.ToImmutableAndFree(), new BoundTryStatement( _factory.Syntax, _factory.Block(statementBuilder.ToImmutableAndFree()), ImmutableArray <BoundCatchBlock> .Empty, _factory.Block(cleanup)))); } else { statementBuilder.AddRange(cleanup); return(_factory.Block(localBuilder.ToImmutableAndFree(), statementBuilder.ToImmutableAndFree())); } }
public override BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { return Previous.InstrumentLocalInitialization(original, rewritten); }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = &v){ ... } == becomes ===> /// /// pinned ref int pinnedTemp = ref v; // pinning managed ref /// int* ptr = (int*)&pinnedTemp; // unsafe cast to unmanaged ptr /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementGetPinnable( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); var initializerType = initializerExpr.Type; var initializerSyntax = initializerExpr.Syntax; var getPinnableMethod = fixedInitializer.GetPinnableOpt; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); // pinned ref int pinnedTemp pinnedTemp = factory.SynthesizedLocal( getPinnableMethod.ReturnType, syntax: declarator, isPinned: true, //NOTE: different from the array and string cases // RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways) refKind: RefKind.RefReadOnly, kind: SynthesizedLocalKind.FixedReference); BoundExpression callReceiver; int currentConditionalAccessID = 0; bool needNullCheck = !initializerType.IsValueType; if (needNullCheck) { currentConditionalAccessID = ++_currentConditionalAccessID; callReceiver = new BoundConditionalReceiver( initializerSyntax, currentConditionalAccessID, initializerType); } else { callReceiver = initializerExpr; } // .GetPinnable() var getPinnableCall = getPinnableMethod.IsStatic ? factory.Call(null, getPinnableMethod, callReceiver) : factory.Call(callReceiver, getPinnableMethod); // temp =ref .GetPinnable() var tempAssignment = factory.AssignmentExpression( factory.Local(pinnedTemp), getPinnableCall, isRef: true); // &pinnedTemp var addr = new BoundAddressOfOperator( factory.Syntax, factory.Local(pinnedTemp), type: fixedInitializer.ElementPointerType); // (int*)&pinnedTemp var pointerValue = factory.Convert( localType, addr, fixedInitializer.ElementPointerTypeConversion); // {pinnedTemp =ref .GetPinnable(), (int*)&pinnedTemp} BoundExpression pinAndGetPtr = factory.Sequence( locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), result: pointerValue); if (needNullCheck) { // initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; pinAndGetPtr = new BoundLoweredConditionalAccess( initializerSyntax, initializerExpr, hasValueMethodOpt: null, whenNotNull: pinAndGetPtr, whenNullOpt: null, // just return default(T*) currentConditionalAccessID, localType); } // ptr = initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), pinAndGetPtr)); return(localInit); }
private BoundStatement InitializeFixedStatementStringLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol stringTemp, out LocalSymbol localToClear) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf<VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString); // NOTE: we pin the string, not the pointer. Debug.Assert(stringTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr); var convertedStringTemp = factory.Convert( localType, factory.Local(stringTemp), fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); localToClear = stringTemp; return factory.Block(stringTempInit, localInit, conditionalAdd); }
/// <summary> /// Lower "using (ResourceType resource = expression) statement" to a try-finally block. /// </summary> /// <remarks> /// Assumes that the local symbol will be declared (i.e. in the LocalsOpt array) of an enclosing block. /// Assumes that using statements with multiple locals have already been split up into multiple using statements. /// </remarks> private BoundBlock RewriteDeclarationUsingStatement(CSharpSyntaxNode usingSyntax, BoundLocalDeclaration localDeclaration, BoundBlock tryBlock, Conversion idisposableConversion) { CSharpSyntaxNode declarationSyntax = localDeclaration.Syntax; LocalSymbol localSymbol = localDeclaration.LocalSymbol; TypeSymbol localType = localSymbol.Type; Debug.Assert((object)localType != null); //otherwise, there wouldn't be a conversion to IDisposable BoundLocal boundLocal = new BoundLocal(declarationSyntax, localSymbol, localDeclaration.InitializerOpt.ConstantValue, localType); BoundStatement rewrittenDeclaration = (BoundStatement)Visit(localDeclaration); // If we know that the expression is null, then we know that the null check in the finally block // will fail, and the Dispose call will never happen. That is, the finally block will have no effect. // Consequently, we can simply skip the whole try-finally construct and just create a block containing // the new declaration. if (boundLocal.ConstantValue == ConstantValue.Null) { //localSymbol will be declared by an enclosing block return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryBlock); } if (localType.IsDynamic()) { BoundExpression tempInit = MakeConversion( declarationSyntax, boundLocal, idisposableConversion, compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); return new BoundBlock( syntax: usingSyntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), //localSymbol will be declared by an enclosing block statements: ImmutableArray.Create<BoundStatement>( rewrittenDeclaration, new BoundExpressionStatement(declarationSyntax, tempAssignment), tryFinally)); } else { BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundLocal); // localSymbol will be declared by an enclosing block return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryFinally); } }
public override BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { return AddDynamicAnalysis(original, base.InstrumentLocalInitialization(original, rewritten)); }
public override BoundStatement InstrumentLocalInitialization(BoundLocalDeclaration original, BoundStatement rewritten) { return(Previous.InstrumentLocalInitialization(original, rewritten)); }
public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) { return(RewriteLocalDeclaration(node.Syntax, node.LocalSymbol, VisitExpression(node.InitializerOpt), node.WasCompilerGenerated, node.HasErrors)); }