/// <summary> /// The flow analysis pass. This pass reports required diagnostics for unreachable /// statements and uninitialized variables (through the call to FlowAnalysisWalker.Analyze), /// and inserts a final return statement if the end of a void-returning method is reachable. /// </summary> /// <param name="method">the method to be analyzed</param> /// <param name="block">the method's body</param> /// <param name="diagnostics">the receiver of the reported diagnostics</param> /// <param name="hasTrailingExpression">indicates whether this Script had a trailing expression</param> /// <param name="originalBodyNested">the original method body is the last statement in the block</param> /// <returns>the rewritten block for the method (with a return statement possibly inserted)</returns> public static BoundBlock Rewrite( MethodSymbol method, BoundBlock block, DiagnosticBag diagnostics, bool hasTrailingExpression, bool originalBodyNested) { #if DEBUG // We should only see a trailingExpression if we're in a Script initializer. Debug.Assert(!hasTrailingExpression || method.IsScriptInitializer); var initialDiagnosticCount = diagnostics.ToReadOnly().Length; #endif var compilation = method.DeclaringCompilation; if (method.ReturnsVoid || method.IsIterator || method.IsTaskReturningAsync(compilation)) { // we don't analyze synthesized void methods. if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) || Analyze(compilation, method, block, diagnostics)) { block = AppendImplicitReturn(block, method, originalBodyNested); } } else if (Analyze(compilation, method, block, diagnostics)) { // If the method is a lambda expression being converted to a non-void delegate type // and the end point is reachable then suppress the error here; a special error // will be reported by the lambda binder. Debug.Assert(method.MethodKind != MethodKind.AnonymousFunction); // Add implicit "return default(T)" if this is a submission that does not have a trailing expression. var submissionResultType = (method as SynthesizedInteractiveInitializerMethod)?.ResultType; if (!hasTrailingExpression && ((object)submissionResultType != null)) { Debug.Assert(submissionResultType.SpecialType != SpecialType.System_Void); var trailingExpression = new BoundDefaultExpression(method.GetNonNullSyntaxNode(), submissionResultType); var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, RefKind.None, trailingExpression)); block = new BoundBlock(block.Syntax, ImmutableArray <LocalSymbol> .Empty, newStatements) { WasCompilerGenerated = true }; #if DEBUG // It should not be necessary to repeat analysis after adding this node, because adding a trailing // return in cases where one was missing should never produce different Diagnostics. IEnumerable <Diagnostic> GetErrorsOnly(IEnumerable <Diagnostic> diags) => diags.Where(d => d.Severity == DiagnosticSeverity.Error); var flowAnalysisDiagnostics = DiagnosticBag.GetInstance(); Debug.Assert(!Analyze(compilation, method, block, flowAnalysisDiagnostics)); // Ignore warnings since flow analysis reports nullability mismatches. Debug.Assert(GetErrorsOnly(flowAnalysisDiagnostics.ToReadOnly()).SequenceEqual(GetErrorsOnly(diagnostics.ToReadOnly().Skip(initialDiagnosticCount)))); flowAnalysisDiagnostics.Free(); #endif } // If there's more than one location, then the method is partial and we // have already reported a non-void partial method error. else if (method.Locations.Length == 1) { diagnostics.Add(ErrorCode.ERR_ReturnExpected, method.Locations[0], method); } } return(block); }
private BoundExpression LiftRangeExpression(BoundRangeExpression node, ImmutableArray <BoundExpression> operands) { Debug.Assert(node.Type.IsNullableType()); Debug.Assert(operands.Any(operand => operand.Type.IsNullableType())); Debug.Assert(operands.Length == 1 || operands.Length == 2); ArrayBuilder <BoundExpression> sideeffects = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <LocalSymbol> locals = ArrayBuilder <LocalSymbol> .GetInstance(); ArrayBuilder <BoundExpression> arguments = ArrayBuilder <BoundExpression> .GetInstance(); // left.HasValue && right.HasValue BoundExpression condition = null; foreach (var operand in operands) { BoundExpression tempOperand = CaptureExpressionInTempIfNeeded(operand, sideeffects, locals); if (tempOperand.Type.IsNullableType()) { BoundExpression operandHasValue = MakeOptimizedHasValue(tempOperand.Syntax, tempOperand); if (condition is null) { condition = operandHasValue; } else { TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); condition = MakeBinaryOperator(node.Syntax, BinaryOperatorKind.BoolAnd, condition, operandHasValue, boolType, method: null); } arguments.Add(MakeOptimizedGetValueOrDefault(tempOperand.Syntax, tempOperand)); } else { arguments.Add(tempOperand); } } Debug.Assert(condition != null); // method(left.GetValueOrDefault(), right.GetValueOrDefault()) BoundExpression rangeCall = MakeCall( node.Syntax, rewrittenReceiver: null, node.MethodOpt, arguments.ToImmutableArray(), node.MethodOpt.ReturnType.TypeSymbol); if (!TryGetNullableMethod(node.Syntax, node.Type, SpecialMember.System_Nullable_T__ctor, out MethodSymbol nullableCtor)) { return(BadExpression(node.Syntax, node.Type, node)); } // new Nullable(method(left.GetValueOrDefault(), right.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(node.Syntax, nullableCtor, binderOpt: null, rangeCall); // default BoundExpression alternative = new BoundDefaultExpression(node.Syntax, constantValueOpt: null, node.Type); // left.HasValue && right.HasValue ? new Nullable(method(left.GetValueOrDefault(), right.GetValueOrDefault())) : default BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: node.Syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: node.Type, isRef: false); return(new BoundSequence( syntax: node.Syntax, locals: locals.ToImmutableAndFree(), sideEffects: sideeffects.ToImmutableAndFree(), value: conditionalExpression, type: node.Type)); }
public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) { Debug.Assert(node.MethodOpt != null); NamedTypeSymbol booleanType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression fromEnd = MakeLiteral(node.Syntax, ConstantValue.Create(true), booleanType); BoundExpression operand = VisitExpression(node.Operand); if (NullableNeverHasValue(operand)) { operand = new BoundDefaultExpression(operand.Syntax, operand.Type.GetNullableUnderlyingType()); } operand = NullableAlwaysHasValue(operand) ?? operand; if (!node.Type.IsNullableType()) { return(new BoundObjectCreationExpression(node.Syntax, node.MethodOpt, binderOpt: null, operand, fromEnd)); } ArrayBuilder <BoundExpression> sideeffects = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <LocalSymbol> locals = ArrayBuilder <LocalSymbol> .GetInstance(); // operand.HasValue operand = CaptureExpressionInTempIfNeeded(operand, sideeffects, locals); BoundExpression condition = MakeOptimizedHasValue(operand.Syntax, operand); // new Index(operand, fromEnd: true) BoundExpression boundOperandGetValueOrDefault = MakeOptimizedGetValueOrDefault(operand.Syntax, operand); BoundExpression indexCreation = new BoundObjectCreationExpression(node.Syntax, node.MethodOpt, binderOpt: null, boundOperandGetValueOrDefault, fromEnd); if (!TryGetNullableMethod(node.Syntax, node.Type, SpecialMember.System_Nullable_T__ctor, out MethodSymbol nullableCtor)) { return(BadExpression(node.Syntax, node.Type, operand)); } // new Nullable(new Index(operand, fromEnd: true)) BoundExpression consequence = new BoundObjectCreationExpression(node.Syntax, nullableCtor, binderOpt: null, indexCreation); // default BoundExpression alternative = new BoundDefaultExpression(node.Syntax, constantValueOpt: null, node.Type); // operand.HasValue ? new Nullable(new Index(operand, fromEnd: true)) : default BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: node.Syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: node.Type, isRef: false); return(new BoundSequence( syntax: node.Syntax, locals: locals.ToImmutableAndFree(), sideEffects: sideeffects.ToImmutableAndFree(), value: conditionalExpression, type: node.Type)); }
public override BoundNode VisitRangeExpression(BoundRangeExpression node) { Debug.Assert(node != null && node.MethodOpt != null); bool needLifting = false; var operandsBuilder = new ArrayBuilder <BoundExpression>(); var left = node.LeftOperand; if (left != null) { left = tryOptimizeOperand(left); operandsBuilder.Add(left); } var right = node.RightOperand; if (right != null) { right = tryOptimizeOperand(right); operandsBuilder.Add(right); } ImmutableArray <BoundExpression> operands = operandsBuilder.ToImmutable(); if (needLifting) { return(LiftRangeExpression(node, operands)); } else { BoundExpression rangeCreation = MakeRangeExpression(node.MethodOpt, left, right); if (node.Type.IsNullableType()) { if (!TryGetNullableMethod(node.Syntax, node.Type, SpecialMember.System_Nullable_T__ctor, out MethodSymbol nullableCtor)) { return(BadExpression(node.Syntax, node.Type, node)); } return(new BoundObjectCreationExpression(node.Syntax, nullableCtor, binderOpt: null, rangeCreation)); } return(rangeCreation); } BoundExpression tryOptimizeOperand(BoundExpression operand) { Debug.Assert(operand != null); operand = VisitExpression(operand); if (NullableNeverHasValue(operand)) { operand = new BoundDefaultExpression(operand.Syntax, operand.Type.GetNullableUnderlyingType()); } else { operand = NullableAlwaysHasValue(operand) ?? operand; if (operand.Type.IsNullableType()) { needLifting = true; } } return(operand); } }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> temps; ImmutableArray <RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments( node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; if (_inExpressionLambda) { if (!temps.IsDefaultOrEmpty) { throw ExceptionUtilities.UnexpectedValue(temps.Length); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType); if (node.Type.IsInterfaceType()) { Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2)); rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false); } return(rewrittenObjectCreation); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: node.Type); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray <BoundExpression> .Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2)); rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return(rewrittenObjectCreation); } return(MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type)); }