private BoundExpression MakeIsOperator( BoundIsOperator oldNode, SyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType ) { if (rewrittenOperand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)rewrittenOperand; BoundExpression?receiver = methodGroup.ReceiverOpt; if (receiver != null && receiver.Kind != BoundKind.ThisReference) { // possible side-effect return(RewriteConstantIsOperator( receiver.Syntax, receiver, ConstantValue.False, rewrittenType )); } else { return(MakeLiteral(syntax, ConstantValue.False, rewrittenType)); } } var operandType = rewrittenOperand.Type; var targetType = rewrittenTargetType.Type; Debug.Assert(operandType is { } || rewrittenOperand.ConstantValue !.IsNull);
private BoundExpression MakeAsOperator( BoundAsOperator oldNode, SyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { // TODO: Handle dynamic operand type and target type Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType)); // target type cannot be a non-nullable value type Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType()); if (!_inExpressionLambda) { ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue); if (constantValue != null) { Debug.Assert(constantValue.IsNull); BoundExpression result = rewrittenType.IsNullableType() ? new BoundDefaultExpression(syntax, rewrittenType) : MakeLiteral(syntax, constantValue, rewrittenType); if (rewrittenOperand.ConstantValue != null) { // No need to preserve any side-effects from the operand. // We also can keep the "constant" notion of the result, which // enables some optimizations down the road. return(result); } return(new BoundSequence( syntax: syntax, locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(rewrittenOperand), value: result, type: rewrittenType)); } if (conversion.IsImplicit) { // Operand with bound implicit conversion to target type. // We don't need a runtime check, generate a conversion for the operand instead. return(MakeConversionNode(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false)); } } return(oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType)); }
private BoundExpression MakeIsOperator( BoundIsOperator oldNode, SyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { if (rewrittenOperand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)rewrittenOperand; BoundExpression receiver = methodGroup.ReceiverOpt; if (receiver != null && receiver.Kind != BoundKind.ThisReference) { // possible side-effect return(RewriteConstantIsOperator(receiver.Syntax, receiver, ConstantValue.False, rewrittenType)); } else { return(MakeLiteral(syntax, ConstantValue.False, rewrittenType)); } } var operandType = rewrittenOperand.Type; var targetType = rewrittenTargetType.Type; Debug.Assert((object)operandType != null || rewrittenOperand.ConstantValue.IsNull); Debug.Assert((object)targetType != null); // TODO: Handle dynamic operand type and target type if (!_inExpressionLambda) { ConstantValue constantValue = Binder.GetIsOperatorConstantResult(operandType, targetType, conversion.Kind, rewrittenOperand.ConstantValue); if (constantValue != null) { return(RewriteConstantIsOperator(syntax, rewrittenOperand, constantValue, rewrittenType)); } else if (conversion.IsImplicit) { // operand is a reference type with bound identity or implicit conversion // We can replace the "is" instruction with a null check return(MakeNullCheck(syntax, rewrittenOperand, BinaryOperatorKind.NotEqual)); } } return(oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType)); }
private BoundExpression MakeAsOperator( BoundAsOperator oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { // TODO: Handle dynamic operand type and target type Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType)); // target type cannot be a non-nullable value type Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType()); if (!_inExpressionLambda) { ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue); if (constantValue != null) { Debug.Assert(constantValue.IsNull); BoundExpression result = rewrittenType.IsNullableType() ? new BoundDefaultOperator(syntax, rewrittenType) : MakeLiteral(syntax, constantValue, rewrittenType); if (rewrittenOperand.ConstantValue != null) { // No need to preserve any side-effects from the operand. // We also can keep the "constant" notion of the result, which // enables some optimizations down the road. return result; } return new BoundSequence( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(rewrittenOperand), value: result, type: rewrittenType); } if (conversion.IsImplicit) { // Operand with bound implicit conversion to target type. // We don't need a runtime check, generate a conversion for the operand instead. return MakeConversionNode(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false); } } return oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType); }
public override BoundNode VisitIsOperator(BoundIsOperator node) { BoundExpression operand = (BoundExpression)this.Visit(node.Operand); BoundTypeExpression targetType = (BoundTypeExpression)this.Visit(node.TargetType); TypeSymbol type = this.VisitType(node.Type); if (operand.Kind != BoundKind.SpillSequence) { return(node.Update(operand, targetType, node.Conversion, type)); } var spill = (BoundSpillSequence)operand; var newIsOperator = node.Update(spill.Value, targetType, node.Conversion, type); return(RewriteSpillSequence(spill, newIsOperator)); }
private void XsInsertMissingOptionalArguments(SyntaxNode syntax, ImmutableArray <ParameterSymbol> parameters, BoundExpression[] arguments, ArrayBuilder <RefKind> refKinds, ArrayBuilder <LocalSymbol> temps, ThreeState enableCallerInfo = ThreeState.Unknown ) { Debug.Assert(refKinds.Count == arguments.Length); for (int p = 0; p < arguments.Length; ++p) { if (arguments[p] == null || arguments[p].Syntax.XIsMissingArgument) { ParameterSymbol parameter = parameters[p]; BoundExpression expr = GetDefaultParameterValue(syntax, parameter, enableCallerInfo); if (expr is BoundDefaultExpression && parameter.Type == _compilation.UsualType()) { var flds = _compilation.UsualType().GetMembers("_NIL"); var type = new BoundTypeExpression(syntax, null, _compilation.UsualType()); expr = _factory.Field(type, (FieldSymbol)flds[0]); } BoundAssignmentOperator boundAssignmentToTemp; BoundLocal boundTemp = _factory.StoreToTemp(expr, out boundAssignmentToTemp); expr = new BoundSequence( syntax, locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(boundAssignmentToTemp), value: boundTemp, type: boundTemp.Type); refKinds[p] = parameters[p].RefKind; temps.Add(boundTemp.LocalSymbol); arguments[p] = expr; Debug.Assert(arguments[p].Type == parameter.Type); if (parameters[p].RefKind == RefKind.In) { Debug.Assert(refKinds[p] == RefKind.None); refKinds[p] = RefKind.In; } } } }
private BoundExpression MakeAsOperator( BoundAsOperator oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { // TODO: Handle dynamic operand type and target type Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType)); // target type cannot be a non-nullable value type Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType()); if (!inExpressionLambda) { ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue); Debug.Assert(constantValue == null || constantValue.IsNull); if (conversion.IsImplicit) { // Operand with bound implicit conversion to target type. // We don't need a runtime check, generate a conversion for the operand instead. return MakeConversion(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false, constantValueOpt: constantValue); } else if (constantValue != null) { return new BoundSequence( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(rewrittenOperand), value: MakeLiteral(syntax, constantValue, rewrittenType), type: rewrittenType); } } return oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType); }
private BoundExpression MakeAsOperator( BoundAsOperator oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { // TODO: Handle dynamic operand type and target type Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType)); // target type cannot be a non-nullable value type Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType()); if (!inExpressionLambda) { ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue); Debug.Assert(constantValue == null || constantValue.IsNull); if (conversion.IsImplicit) { // Operand with bound implicit conversion to target type. // We don't need a runtime check, generate a conversion for the operand instead. return(MakeConversion(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false, constantValueOpt: constantValue)); } else if (constantValue != null) { return(new BoundSequence( syntax: syntax, locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(rewrittenOperand), value: MakeLiteral(syntax, constantValue, rewrittenType), type: rewrittenType)); } } return(oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType)); }
private BoundExpression MakeIsOperator( BoundIsOperator oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { if (rewrittenOperand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)rewrittenOperand; BoundExpression receiver = methodGroup.ReceiverOpt; if (receiver != null && receiver.Kind != BoundKind.ThisReference) { // possible side-effect return RewriteConstantIsOperator(receiver.Syntax, receiver, ConstantValue.False, rewrittenType); } else { return MakeLiteral(syntax, ConstantValue.False, rewrittenType); } } var operandType = rewrittenOperand.Type; var targetType = rewrittenTargetType.Type; Debug.Assert((object)operandType != null || rewrittenOperand.ConstantValue.IsNull); Debug.Assert((object)targetType != null); // TODO: Handle dynamic operand type and target type if (!inExpressionLambda) { ConstantValue constantValue = Binder.GetIsOperatorConstantResult(operandType, targetType, conversion.Kind, rewrittenOperand.ConstantValue); if (constantValue != null) { return RewriteConstantIsOperator(syntax, rewrittenOperand, constantValue, rewrittenType); } else if (conversion.IsImplicit) { // operand is a reference type with bound identity or implicit conversion // We can replace the "is" instruction with a null check Debug.Assert((object)operandType != null); if (operandType.TypeKind == TypeKind.TypeParameter) { // We need to box the type parameter even if it is a known // reference type to ensure there are no verifier errors rewrittenOperand = MakeConversion( syntax: rewrittenOperand.Syntax, rewrittenOperand: rewrittenOperand, conversionKind: ConversionKind.Boxing, rewrittenType: compilation.GetSpecialType(SpecialType.System_Object), @checked: false); } return MakeNullCheck(syntax, rewrittenOperand, BinaryOperatorKind.NotEqual); } } return oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType); }
private BoundPattern BindDeclarationPattern( DeclarationPatternSyntax node, BoundExpression operand, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) { Debug.Assert(operand != null && operandType != (object)null); var typeSyntax = node.Type; bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar) { declType = operandType; } if (declType == (object)null) { Debug.Assert(hasErrors); declType = this.CreateErrorType("var"); } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) { hasErrors = true; } else { hasErrors |= CheckValidPatternType(typeSyntax, operand, operandType, declType, isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } switch (node.Designation.Kind()) { case SyntaxKind.SingleVariableDesignation: break; case SyntaxKind.DiscardedDesignation: return new BoundDeclarationPattern(node, null, boundDeclType, isVar, hasErrors); default: throw ExceptionUtilities.UnexpectedValue(node.Designation.Kind()); } var designation = (SingleVariableDesignationSyntax)node.Designation; var identifier = designation.Identifier; SourceLocalSymbol localSymbol = this.LookupLocal(identifier); if (localSymbol != (object)null) { if (InConstructorInitializer || InFieldInitializer) { Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node); } localSymbol.SetType(declType); // Check for variable declaration errors. hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); if (!hasErrors) { hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax); } return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors); } else { // We should have the right binder in the chain for a script or interactive, so we use the field for the pattern. Debug.Assert(node.SyntaxTree.Options.Kind != SourceCodeKind.Regular); GlobalExpressionVariable expressionVariableField = LookupDeclaredField(designation); DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance(); expressionVariableField.SetType(declType, tempDiagnostics); tempDiagnostics.Free(); BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics); var variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors); return new BoundDeclarationPattern(node, expressionVariableField, variableAccess, boundDeclType, isVar, hasErrors); } }
/// <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); }
public BoundExpression VisitDynamicInvocation(BoundDynamicInvocation node, bool resultDiscarded) { var loweredArguments = VisitList(node.Arguments); bool hasImplicitReceiver; BoundExpression loweredReceiver; ImmutableArray<TypeSymbol> typeArguments; string name; switch (node.Expression.Kind) { case BoundKind.MethodGroup: // method invocation BoundMethodGroup methodGroup = (BoundMethodGroup)node.Expression; typeArguments = methodGroup.TypeArgumentsOpt; name = methodGroup.Name; hasImplicitReceiver = (methodGroup.Flags & BoundMethodGroupFlags.HasImplicitReceiver) != 0; // Should have been eliminated during binding of dynamic invocation: Debug.Assert(methodGroup.ReceiverOpt == null || methodGroup.ReceiverOpt.Kind != BoundKind.TypeOrValueExpression); if (methodGroup.ReceiverOpt == null) { // Calling a static method defined on an outer class via its simple name. NamedTypeSymbol firstContainer = node.ApplicableMethods.First().ContainingType; Debug.Assert(node.ApplicableMethods.All(m => m.IsStatic && m.ContainingType == firstContainer)); loweredReceiver = new BoundTypeExpression(node.Syntax, null, firstContainer); } else if (hasImplicitReceiver && _factory.TopLevelMethod.IsStatic) { // Calling a static method defined on the current class via its simple name. loweredReceiver = new BoundTypeExpression(node.Syntax, null, _factory.CurrentType); } else { loweredReceiver = VisitExpression(methodGroup.ReceiverOpt); } // If we are calling a method on a NoPIA type, we need to embed all methods/properties // with the matching name of this dynamic invocation. EmbedIfNeedTo(loweredReceiver, methodGroup.Methods, node.Syntax); break; case BoundKind.DynamicMemberAccess: // method invocation var memberAccess = (BoundDynamicMemberAccess)node.Expression; name = memberAccess.Name; typeArguments = memberAccess.TypeArgumentsOpt; loweredReceiver = VisitExpression(memberAccess.Receiver); hasImplicitReceiver = false; break; default: // delegate invocation var loweredExpression = VisitExpression(node.Expression); return _dynamicFactory.MakeDynamicInvocation(loweredExpression, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, resultDiscarded).ToExpression(); } Debug.Assert(loweredReceiver != null); return _dynamicFactory.MakeDynamicMemberInvocation( name, loweredReceiver, typeArguments, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, hasImplicitReceiver, resultDiscarded).ToExpression(); }
private BoundPattern BindDeclarationPattern( DeclarationPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) { Debug.Assert(operandType != (object)null); var typeSyntax = node.Type; bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar) { declType = operandType; } if (declType == (object)null) { Debug.Assert(hasErrors); declType = this.CreateErrorType("var"); } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) { hasErrors = true; } else { hasErrors |= CheckValidPatternType(typeSyntax, operandType, declType, isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } switch (node.Designation.Kind()) { case SyntaxKind.SingleVariableDesignation: break; case SyntaxKind.DiscardDesignation: return(new BoundDeclarationPattern(node, null, boundDeclType, isVar, hasErrors)); default: throw ExceptionUtilities.UnexpectedValue(node.Designation.Kind()); } var designation = (SingleVariableDesignationSyntax)node.Designation; var identifier = designation.Identifier; SourceLocalSymbol localSymbol = this.LookupLocal(identifier); if (localSymbol != (object)null) { if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType) { CheckFeatureAvailability(node, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics); } localSymbol.SetType(declType); // Check for variable declaration errors. hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); if (!hasErrors) { hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax); } return(new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors)); } else { // We should have the right binder in the chain for a script or interactive, so we use the field for the pattern. Debug.Assert(node.SyntaxTree.Options.Kind != SourceCodeKind.Regular); GlobalExpressionVariable expressionVariableField = LookupDeclaredField(designation); DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance(); expressionVariableField.SetType(declType, tempDiagnostics); tempDiagnostics.Free(); BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics); var variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors); return(new BoundDeclarationPattern(node, expressionVariableField, variableAccess, boundDeclType, isVar, hasErrors)); } }
public override BoundNode VisitTypeExpression(BoundTypeExpression node) { var result = base.VisitTypeExpression(node); Debug.Assert(result is { });
/// <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; }
public BoundDeclarationPattern(SyntaxNode syntax, LocalSymbol localSymbol, BoundTypeExpression declaredType, bool isVar, bool hasErrors = false) : this(syntax, localSymbol, localSymbol == null ? new BoundDiscardExpression(syntax, declaredType.Type) : (BoundExpression) new BoundLocal(syntax, localSymbol, null, declaredType.Type), declaredType, isVar, hasErrors) { }
public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, BoundTypeExpression boundContainingTypeOpt, ImmutableArray <BoundExpression> boundDimensionsOpt, TypeWithAnnotations typeWithAnnotations, bool hasErrors = false) : this(syntax, aliasOpt, boundContainingTypeOpt, boundDimensionsOpt, typeWithAnnotations, typeWithAnnotations.Type, hasErrors) { Debug.Assert((object)typeWithAnnotations.Type != null, "Field 'type' cannot be null"); }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics) { BoundExpression collectionExpr = this.Next.BindValue(syntax.Expression, diagnostics, BindValueKind.RValue); //bind with next to avoid seeing iteration variable ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! hasErrors |= this.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = this.syntax.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); TypeSymbol iterationVariableType; if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); BoundStatement body = BindPossibleEmbeddedStatement(syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error. if (hasErrors) { return(new BoundForEachStatement( syntax, ImmutableArray <LocalSymbol> .Empty, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors)); } var foreachKeyword = syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics); if (!elementConversion.IsValid) { ImmutableArray <MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax), ref useSiteDiagnostics); diagnostics.Add(syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error, "Conversions to object succeed unless there's a problem with the object type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return(new BoundForEachStatement( syntax, ImmutableArray <LocalSymbol> .Empty, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors)); }
private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBag diagnostics) { var resultType = (TypeSymbol)GetSpecialType(SpecialType.System_Boolean, diagnostics, node); var operand = BindValue(node.Left, diagnostics, BindValueKind.RValue); AliasSymbol alias; TypeSymbol targetType; { // try binding as a type, but back off to binding as an expression if that does not work. var tempBag = DiagnosticBag.GetInstance(); targetType = BindType(node.Right, tempBag, out alias); if (targetType?.IsErrorType() == true && tempBag.HasAnyResolvedErrors() && ((CSharpParseOptions)node.SyntaxTree.Options).IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching)) { // it did not bind as a type; try binding as a constant expression pattern bool wasExpression; var tempBag2 = DiagnosticBag.GetInstance(); var boundConstantPattern = BindConstantPattern( node, operand, operand.Type, node.Right, node.Right.HasErrors, tempBag2, out wasExpression, wasSwitchCase: false); if (wasExpression) { tempBag.Free(); diagnostics.AddRangeAndFree(tempBag2); return new BoundIsPatternExpression(node, operand, boundConstantPattern, resultType); } tempBag2.Free(); } diagnostics.AddRangeAndFree(tempBag); } var typeExpression = new BoundTypeExpression(node.Right, alias, targetType); var targetTypeKind = targetType.TypeKind; if (IsOperandErrors(node, operand, diagnostics) || IsOperatorErrors(node, operand.Type, typeExpression, diagnostics)) { return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } // Is and As operator should have null ConstantValue as they are not constant expressions. // However we perform analysis of is/as expressions at bind time to detect if the expression // will always evaluate to a constant to generate warnings (always true/false/null). // We also need this analysis result during rewrite to optimize away redundant isinst instructions. // We store the conversion from expression's operand type to target type to enable these // optimizations during is/as operator rewrite. HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (operand.ConstantValue == ConstantValue.Null || operand.Kind == BoundKind.MethodGroup || operand.Type.SpecialType == SpecialType.System_Void) { // warning for cases where the result is always false: // (a) "null is TYPE" OR operand evaluates to null // (b) operand is a MethodGroup // (c) operand is of void type // NOTE: Dev10 violates the SPEC for case (c) above and generates // NOTE: an error ERR_NoExplicitBuiltinConv if the target type // NOTE: is an open type. According to the specification, the result // NOTE: is always false, but no compile time error occurs. // NOTE: We follow the specification and generate WRN_IsAlwaysFalse // NOTE: instead of an error. // NOTE: See Test SyntaxBinderTests.TestIsOperatorWithTypeParameter Error(diagnostics, ErrorCode.WRN_IsAlwaysFalse, node, targetType); Conversion conv = Conversions.ClassifyConversionFromExpression(operand, targetType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); return new BoundIsOperator(node, operand, typeExpression, conv, resultType); } if (targetTypeKind == TypeKind.Dynamic) { // warning for dynamic target type Error(diagnostics, ErrorCode.WRN_IsDynamicIsConfusing, node, node.OperatorToken.Text, targetType.Name, GetSpecialType(SpecialType.System_Object, diagnostics, node).Name // a pretty way of getting the string "Object" ); } var operandType = operand.Type; Debug.Assert((object)operandType != null); if (operandType.TypeKind == TypeKind.Dynamic) { // if operand has a dynamic type, we do the same thing as though it were an object operandType = GetSpecialType(SpecialType.System_Object, diagnostics, node); } Conversion conversion = Conversions.ClassifyConversion(operandType, targetType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); ReportIsOperatorConstantWarnings(node, diagnostics, operandType, targetType, conversion.Kind, operand.ConstantValue); return new BoundIsOperator(node, operand, typeExpression, conversion, resultType); }
private BoundPattern BindDeclarationPattern( DeclarationPatternSyntax node, BoundExpression operand, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) { Debug.Assert(operand != null || operandType != (object)null); var typeSyntax = node.Type; var identifier = node.Identifier; bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar && operandType != (object)null) { declType = operandType; } if (declType == (object)null) { Debug.Assert(hasErrors); declType = this.CreateErrorType(); } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) { hasErrors = true; } else { hasErrors |= CheckValidPatternType(typeSyntax, operand, operandType, declType, isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } SourceLocalSymbol localSymbol = this.LookupLocal(identifier); // In error scenarios with misplaced code, it is possible we can't bind the local declaration. // This occurs through the semantic model. In that case concoct a plausible result. if (localSymbol == (object)null) { localSymbol = SourceLocalSymbol.MakeLocal( ContainingMemberOrLambda, this, RefKind.None, typeSyntax, identifier, LocalDeclarationKind.PatternVariable); } if (isVar) { localSymbol.SetTypeSymbol(operandType); } // Check for variable declaration errors. hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declType.IsRestrictedType() && !hasErrors) { Error(diagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declType); hasErrors = true; } DeclareLocalVariable(localSymbol, identifier, declType); return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors); }
public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, BoundTypeExpression boundContainingTypeOpt, TypeWithAnnotations typeWithAnnotations, bool hasErrors = false) : this(syntax, aliasOpt, boundContainingTypeOpt, ImmutableArray <BoundExpression> .Empty, typeWithAnnotations, hasErrors) { }
protected BoundLocalDeclaration BindVariableDeclaration( LocalDeclarationKind kind, bool isVar, VariableDeclaratorSyntax declarator, TypeSyntax typeSyntax, TypeSymbol declTypeOpt, AliasSymbol aliasOpt, DiagnosticBag diagnostics, CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); Debug.Assert((object)declTypeOpt != null || isVar); Debug.Assert(typeSyntax != null); // if we are not given desired syntax, we use declarator associatedSyntaxNode = associatedSyntaxNode ?? declarator; bool hasErrors = false; BoundExpression initializerOpt; SourceLocalSymbol localSymbol = this.LookupLocal(declarator.Identifier); // In error scenarios with misplaced code, it is possible we can't bind the local declaration. // This occurs through the semantic model. In that case concoct a plausible result. if ((object)localSymbol == null) { localSymbol = SourceLocalSymbol.MakeLocal( ContainingMemberOrLambda as MethodSymbol, this, typeSyntax, declarator.Identifier, declarator.Initializer, LocalDeclarationKind.Variable); } // Check for variable declaration errors. hasErrors |= this.EnsureDeclarationInvariantMeaningInScope(localSymbol, diagnostics); EqualsValueClauseSyntax equalsValueClauseSyntax = declarator.Initializer; if (isVar) { aliasOpt = null; var binder = new ImplicitlyTypedLocalBinder(this, localSymbol); initializerOpt = binder.BindInferredVariableInitializer(diagnostics, equalsValueClauseSyntax, declarator); // If we got a good result then swap the inferred type for the "var" if (initializerOpt != null && (object)initializerOpt.Type != null) { declTypeOpt = initializerOpt.Type; if (declTypeOpt.SpecialType == SpecialType.System_Void) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, declarator, declTypeOpt); declTypeOpt = CreateErrorType("var"); hasErrors = true; } if (!declTypeOpt.IsErrorType()) { if (declTypeOpt.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, initializerOpt.Type); hasErrors = true; } } } else { declTypeOpt = CreateErrorType("var"); hasErrors = true; } } else { if (ReferenceEquals(equalsValueClauseSyntax, null)) { initializerOpt = null; } else { // Basically inlined BindVariableInitializer, but with conversion optional. initializerOpt = BindPossibleArrayInitializer(equalsValueClauseSyntax.Value, declTypeOpt, diagnostics); if (kind != LocalDeclarationKind.Fixed) { // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, diagnostics); } } } Debug.Assert((object)declTypeOpt != null); if (kind == LocalDeclarationKind.Fixed) { // NOTE: this is an error, but it won't prevent further binding. if (isVar) { if (!hasErrors) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, declarator); hasErrors = true; } } if (!declTypeOpt.IsPointerType()) { if (!hasErrors) { Error(diagnostics, ErrorCode.ERR_BadFixedInitType, declarator); hasErrors = true; } } else if (!IsValidFixedVariableInitializer(declTypeOpt, localSymbol, ref initializerOpt, diagnostics)) { hasErrors = true; } } if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declTypeOpt.IsRestrictedType()) { Error(diagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declTypeOpt); hasErrors = true; } DeclareLocalVariable( localSymbol, declarator.Identifier, declTypeOpt); Debug.Assert((object)localSymbol != null); // It is possible that we have a bracketed argument list, like "int x[];" or "int x[123];" // in a non-fixed-size-array declaration . This is a common error made by C++ programmers. // We have already given a good error at parse time telling the user to either make it "fixed" // or to move the brackets to the type. However, we should still do semantic analysis of // the arguments, so that errors in them are discovered, hovering over them in the IDE // gives good results, and so on. var arguments = default(ImmutableArray<BoundExpression>); if (declarator.ArgumentList != null) { var builder = ArrayBuilder<BoundExpression>.GetInstance(); foreach (var argument in declarator.ArgumentList.Arguments) { var boundArgument = BindValue(argument.Expression, diagnostics, BindValueKind.RValue); builder.Add(boundArgument); } arguments = builder.ToImmutableAndFree(); } if (kind == LocalDeclarationKind.Fixed || kind == LocalDeclarationKind.Using) { // CONSIDER: The error message is "you must provide an initializer in a fixed // CONSIDER: or using declaration". The error message could be targetted to // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) { Error(diagnostics, ErrorCode.ERR_FixedMustInit, declarator); hasErrors = true; } } else if (kind == LocalDeclarationKind.Constant && initializerOpt != null) { foreach (var diagnostic in localSymbol.GetConstantValueDiagnostics(initializerOpt)) { diagnostics.Add(diagnostic); hasErrors = true; } } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt); return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors); }
private BoundPattern BindDeclarationPattern( DeclarationPatternSyntax node, BoundExpression operand, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) { Debug.Assert(operand != null && operandType != (object)null); var typeSyntax = node.Type; var identifier = node.Identifier; bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar) { declType = operandType; } if (declType == (object)null) { Debug.Assert(hasErrors); declType = this.CreateErrorType("var"); } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) { hasErrors = true; } else { hasErrors |= CheckValidPatternType(typeSyntax, operand, operandType, declType, isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } SourceLocalSymbol localSymbol = this.LookupLocal(identifier); if (localSymbol != (object)null) { if (InConstructorInitializer || InFieldInitializer) { Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node); } localSymbol.SetType(declType); // Check for variable declaration errors. hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); if (!hasErrors) { hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax); } return(new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors)); } else { // We should have the right binder in the chain for a script or interactive, so we use the field for the pattern. Debug.Assert(node.SyntaxTree.Options.Kind != SourceCodeKind.Regular); GlobalExpressionVariable expressionVariableField = LookupDeclaredField(node); DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance(); expressionVariableField.SetType(declType, tempDiagnostics); tempDiagnostics.Free(); BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics); var variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors); return(new BoundDeclarationPattern(node, expressionVariableField, variableAccess, boundDeclType, isVar, hasErrors)); } }
private BoundPattern BindDeclarationPattern( DeclarationPatternSyntax node, BoundExpression operand, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) { Debug.Assert(operand != null || operandType != (object)null); if (InConstructorInitializer || InFieldInitializer) { Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node); } var typeSyntax = node.Type; var identifier = node.Identifier; bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar && operandType != (object)null) { declType = operandType; } if (declType == (object)null) { Debug.Assert(hasErrors); declType = this.CreateErrorType("var"); } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) { hasErrors = true; } else { hasErrors |= CheckValidPatternType(typeSyntax, operand, operandType, declType, isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } SourceLocalSymbol localSymbol = this.LookupLocal(identifier); // In error scenarios with misplaced code, it is possible we can't bind the local declaration. // This occurs through the semantic model. In that case concoct a plausible result. if (localSymbol == (object)null) { localSymbol = SourceLocalSymbol.MakeLocal( ContainingMemberOrLambda, this, false, // do not allow ref typeSyntax, identifier, LocalDeclarationKind.PatternVariable); } localSymbol.SetType(declType); // Check for variable declaration errors. hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declType.IsRestrictedType() && !hasErrors) { Error(diagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declType); hasErrors = true; } return(new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors)); }
protected BoundLocalDeclaration BindVariableDeclaration( SourceLocalSymbol localSymbol, LocalDeclarationKind kind, bool isVar, VariableDeclaratorSyntax declarator, TypeSyntax typeSyntax, TypeSymbol declTypeOpt, AliasSymbol aliasOpt, DiagnosticBag diagnostics, CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); Debug.Assert((object)declTypeOpt != null || isVar); Debug.Assert(typeSyntax != null); var localDiagnostics = DiagnosticBag.GetInstance(); // if we are not given desired syntax, we use declarator associatedSyntaxNode = associatedSyntaxNode ?? declarator; bool hasErrors = false; BoundExpression initializerOpt; // Check for variable declaration errors. hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, localDiagnostics); EqualsValueClauseSyntax equalsValueClauseSyntax = declarator.Initializer; if (isVar) { aliasOpt = null; var binder = new ImplicitlyTypedLocalBinder(this, localSymbol); initializerOpt = binder.BindInferredVariableInitializer(localDiagnostics, equalsValueClauseSyntax, declarator); // If we got a good result then swap the inferred type for the "var" if ((object)initializerOpt?.Type != null) { declTypeOpt = initializerOpt.Type; if (declTypeOpt.SpecialType == SpecialType.System_Void) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, declarator, declTypeOpt); declTypeOpt = CreateErrorType("var"); hasErrors = true; } if (!declTypeOpt.IsErrorType()) { if (declTypeOpt.IsStatic) { Error(localDiagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, initializerOpt.Type); hasErrors = true; } } } else { declTypeOpt = CreateErrorType("var"); hasErrors = true; } } else { if (ReferenceEquals(equalsValueClauseSyntax, null)) { initializerOpt = null; } else { // Basically inlined BindVariableInitializer, but with conversion optional. initializerOpt = BindPossibleArrayInitializer(equalsValueClauseSyntax.Value, declTypeOpt, localDiagnostics); if (kind != LocalDeclarationKind.FixedVariable) { // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, localDiagnostics); } } } Debug.Assert((object)declTypeOpt != null); if (kind == LocalDeclarationKind.FixedVariable) { // NOTE: this is an error, but it won't prevent further binding. if (isVar) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, declarator); hasErrors = true; } } if (!declTypeOpt.IsPointerType()) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_BadFixedInitType, declarator); hasErrors = true; } } else if (!IsValidFixedVariableInitializer(declTypeOpt, localSymbol, ref initializerOpt, localDiagnostics)) { hasErrors = true; } } if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declTypeOpt.IsRestrictedType()) { Error(localDiagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declTypeOpt); hasErrors = true; } DeclareLocalVariable( localSymbol, declarator.Identifier, declTypeOpt); Debug.Assert((object)localSymbol != null); ImmutableArray<BoundExpression> arguments = BindDeclaratorArguments(declarator, localDiagnostics); if (kind == LocalDeclarationKind.FixedVariable || kind == LocalDeclarationKind.UsingVariable) { // CONSIDER: The error message is "you must provide an initializer in a fixed // CONSIDER: or using declaration". The error message could be targetted to // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) { Error(localDiagnostics, ErrorCode.ERR_FixedMustInit, declarator); hasErrors = true; } } else if (kind == LocalDeclarationKind.Constant && initializerOpt != null && !localDiagnostics.HasAnyResolvedErrors()) { var constantValueDiagnostics = localSymbol.GetConstantValueDiagnostics(initializerOpt); foreach (var diagnostic in constantValueDiagnostics) { diagnostics.Add(diagnostic); hasErrors = true; } } diagnostics.AddRangeAndFree(localDiagnostics); var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt); return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors); }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder) { // Use the right binder to avoid seeing iteration variable BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! var hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = _syntax.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); TypeSymbol iterationVariableType; if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts). if (hasErrors) { return new BoundForEachStatement( _syntax, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); } hasErrors |= hasNameConflicts; var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics); if (!elementConversion.IsValid) { ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error || !useSiteDiagnostics.IsNullOrEmpty(), "Conversions to object succeed unless there's a problem with the object type or the source type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return new BoundForEachStatement( _syntax, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); }
private bool IsOperatorErrors(CSharpSyntaxNode node, TypeSymbol operandType, BoundTypeExpression typeExpression, DiagnosticBag diagnostics) { var targetType = typeExpression.Type; var targetTypeKind = targetType.TypeKind; // The native compiler allows "x is C" where C is a static class. This // is strictly illegal according to the specification (see the section // called "Referencing Static Class Types".) To retain compatibility we // allow it, but when /feature:strict is enabled we break with the native // compiler and turn this into an error, as it should be. if (targetType.IsStatic && Compilation.FeatureStrictEnabled) { Error(diagnostics, ErrorCode.ERR_StaticInAsOrIs, node, targetType); return true; } if ((object)operandType != null && operandType.TypeKind == TypeKind.Pointer || targetTypeKind == TypeKind.Pointer) { // operand for an is or as expression cannot be of pointer type Error(diagnostics, ErrorCode.ERR_PointerInAsOrIs, node); return true; } return targetTypeKind == TypeKind.Error; }
private BoundExpression MakeIsOperator( BoundIsOperator oldNode, SyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, Conversion conversion, TypeSymbol rewrittenType) { if (rewrittenOperand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)rewrittenOperand; BoundExpression receiver = methodGroup.ReceiverOpt; if (receiver != null && receiver.Kind != BoundKind.ThisReference) { // possible side-effect return(RewriteConstantIsOperator(receiver.Syntax, receiver, ConstantValue.False, rewrittenType)); } else { return(MakeLiteral(syntax, ConstantValue.False, rewrittenType)); } } var operandType = rewrittenOperand.Type; var targetType = rewrittenTargetType.Type; Debug.Assert((object)operandType != null || rewrittenOperand.ConstantValue.IsNull); Debug.Assert((object)targetType != null); // TODO: Handle dynamic operand type and target type if (!_inExpressionLambda) { ConstantValue constantValue = Binder.GetIsOperatorConstantResult(operandType, targetType, conversion.Kind, rewrittenOperand.ConstantValue); if (constantValue != null) { return(RewriteConstantIsOperator(syntax, rewrittenOperand, constantValue, rewrittenType)); } else if (conversion.IsImplicit) { // operand is a reference type with bound identity or implicit conversion // We can replace the "is" instruction with a null check Debug.Assert((object)operandType != null); if (operandType.TypeKind == TypeKind.TypeParameter) { // We need to box the type parameter even if it is a known // reference type to ensure there are no verifier errors rewrittenOperand = MakeConversionNode( syntax: rewrittenOperand.Syntax, rewrittenOperand: rewrittenOperand, conversion: Conversion.Boxing, rewrittenType: _compilation.GetSpecialType(SpecialType.System_Object), @checked: false); } return(MakeNullCheck(syntax, rewrittenOperand, BinaryOperatorKind.NotEqual)); } } return(oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType)); }
private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBag diagnostics) { var operand = BindValue(node.Left, diagnostics, BindValueKind.RValue); AliasSymbol alias; var targetType = BindType(node.Right, diagnostics, out alias); var typeExpression = new BoundTypeExpression(node.Right, alias, targetType); var targetTypeKind = targetType.TypeKind; var resultType = targetType; // Is and As operator should have null ConstantValue as they are not constant expressions. // However we perform analysis of is/as expressions at bind time to detect if the expression // will always evaluate to a constant to generate warnings (always true/false/null). // We also need this analysis result during rewrite to optimize away redundant isinst instructions. // We store the conversion kind from expression's operand type to target type to enable these // optimizations during is/as operator rewrite. switch (operand.Kind) { case BoundKind.UnboundLambda: case BoundKind.Lambda: case BoundKind.MethodGroup: // New in Roslyn - see DevDiv #864740. // operand for an is or as expression cannot be a lambda expression or method group if (!operand.HasAnyErrors) { Error(diagnostics, ErrorCode.ERR_LambdaInIsAs, node); } return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } if (operand.HasAnyErrors || targetTypeKind == TypeKind.Error) { // If either operand is bad or target type has errors, bail out preventing more cascading errors. return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } // SPEC: In an operation of the form E as T, E must be an expression and T must be a // SPEC: reference type, a type parameter known to be a reference type, or a nullable type. if (!targetType.IsReferenceType && !targetType.IsNullableType()) { // target type for an as expression cannot be a non-nullable value type. // generate appropriate error if (targetTypeKind == TypeKind.TypeParameter) { Error(diagnostics, ErrorCode.ERR_AsWithTypeVar, node, targetType); } else if (targetTypeKind == TypeKind.Pointer) { Error(diagnostics, ErrorCode.ERR_PointerInAsOrIs, node); } else { Error(diagnostics, ErrorCode.ERR_AsMustHaveReferenceType, node, targetType); } return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } // The C# specification states in the section called // "Referencing Static Class Types" that it is always // illegal to use "as" with a static type. The // native compiler actually allows "null as C" for // a static type C to be an expression of type C. // It also allows "someObject as C" if "someObject" // is of type object. To retain compatibility we // allow it, but when /feature:strict is enabled we break with the native // compiler and turn this into an error, as it should be. if (targetType.IsStatic && Compilation.FeatureStrictEnabled) { Error(diagnostics, ErrorCode.ERR_StaticInAsOrIs, node, targetType); return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } if (operand.IsLiteralNull()) { // We do not want to warn for the case "null as TYPE" where the null // is a literal, because the user might be saying it to cause overload resolution // to pick a particular method return new BoundAsOperator(node, operand, typeExpression, Conversion.NullLiteral, resultType); } if (operand.Kind == BoundKind.MethodGroup) { Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType); return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } var operandType = operand.Type; Debug.Assert((object)operandType != null); var operandTypeKind = operandType.TypeKind; Debug.Assert(targetTypeKind != TypeKind.Pointer, "Should have been caught above"); if (operandTypeKind == TypeKind.Pointer) { // operand for an is or as expression cannot be of pointer type Error(diagnostics, ErrorCode.ERR_PointerInAsOrIs, node); return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } if (operandTypeKind == TypeKind.Dynamic) { // if operand has a dynamic type, we do the same thing as though it were an object operandType = GetSpecialType(SpecialType.System_Object, diagnostics, node); operandTypeKind = operandType.TypeKind; } if (targetTypeKind == TypeKind.Dynamic) { // for "as dynamic", we do the same thing as though it were an "as object" targetType = GetSpecialType(SpecialType.System_Object, diagnostics, node); targetTypeKind = targetType.TypeKind; } HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = Conversions.ClassifyConversion(operandType, targetType, ref useSiteDiagnostics, builtinOnly: true); diagnostics.Add(node, useSiteDiagnostics); bool hasErrors = ReportAsOperatorConversionDiagnostics(node, diagnostics, this.Compilation, operandType, targetType, conversion.Kind, operand.ConstantValue); return new BoundAsOperator(node, operand, typeExpression, conversion, resultType, hasErrors); }
private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBag diagnostics) { var operand = BindValue(node.Left, diagnostics, BindValueKind.RValue); AliasSymbol alias; TypeSymbol targetType = BindType(node.Right, diagnostics, out alias); var typeExpression = new BoundTypeExpression(node.Right, alias, targetType); var targetTypeKind = targetType.TypeKind; var resultType = (TypeSymbol)GetSpecialType(SpecialType.System_Boolean, diagnostics, node); // Is and As operator should have null ConstantValue as they are not constant expressions. // However we perform analysis of is/as expressions at bind time to detect if the expression // will always evaluate to a constant to generate warnings (always true/false/null). // We also need this analysis result during rewrite to optimize away redundant isinst instructions. // We store the conversion from expression's operand type to target type to enable these // optimizations during is/as operator rewrite. switch (operand.Kind) { case BoundKind.UnboundLambda: case BoundKind.Lambda: case BoundKind.MethodGroup: // New in Roslyn - see DevDiv #864740. // operand for an is or as expression cannot be a lambda expression or method group Error(diagnostics, ErrorCode.ERR_LambdaInIsAs, node); return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } if (operand.HasAnyErrors || targetTypeKind == TypeKind.Error) { // If either operand is bad or target type has errors, bail out preventing more cascading errors. return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } // The native compiler allows "x is C" where C is a static class. This // is strictly illegal according to the specification (see the section // called "Referencing Static Class Types".) To retain compatibility we // allow it, but when /feature:strict is enabled we break with the native // compiler and turn this into an error, as it should be. if (targetType.IsStatic && Compilation.FeatureStrictEnabled) { Error(diagnostics, ErrorCode.ERR_StaticInAsOrIs, node, targetType); return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } var operandType = operand.Type; if ((object)operandType != null && operandType.TypeKind == TypeKind.Pointer || targetTypeKind == TypeKind.Pointer) { // operand for an is or as expression cannot be of pointer type Error(diagnostics, ErrorCode.ERR_PointerInAsOrIs, node); return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); } HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (operand.ConstantValue == ConstantValue.Null || operand.Kind == BoundKind.MethodGroup || operandType.SpecialType == SpecialType.System_Void) { // warning for cases where the result is always false: // (a) "null is TYPE" OR operand evaluates to null // (b) operand is a MethodGroup // (c) operand is of void type // NOTE: Dev10 violates the SPEC for case (c) above and generates // NOTE: an error ERR_NoExplicitBuiltinConv if the target type // NOTE: is an open type. According to the specification, the result // NOTE: is always false, but no compile time error occurs. // NOTE: We follow the specification and generate WRN_IsAlwaysFalse // NOTE: instead of an error. // NOTE: See Test SyntaxBinderTests.TestIsOperatorWithTypeParameter Error(diagnostics, ErrorCode.WRN_IsAlwaysFalse, node, targetType); Conversion conv = Conversions.ClassifyConversionFromExpression(operand, targetType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); return new BoundIsOperator(node, operand, typeExpression, conv, resultType); } if (targetTypeKind == TypeKind.Dynamic) { // warning for dynamic target type Error(diagnostics, ErrorCode.WRN_IsDynamicIsConfusing, node, node.OperatorToken.Text, targetType.Name, GetSpecialType(SpecialType.System_Object, diagnostics, node).Name // a pretty way of getting the string "Object" ); } Debug.Assert((object)operandType != null); if (operandType.TypeKind == TypeKind.Dynamic) { // if operand has a dynamic type, we do the same thing as though it were an object operandType = GetSpecialType(SpecialType.System_Object, diagnostics, node); } Conversion conversion = Conversions.ClassifyConversion(operandType, targetType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); ReportIsOperatorConstantWarnings(node, diagnostics, operandType, targetType, conversion.Kind, operand.ConstantValue); return new BoundIsOperator(node, operand, typeExpression, conversion, resultType); }
public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo enumeratorInfoOpt, Conversion elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray <LocalSymbol> iterationVariables, BoundExpression expression, BoundForEachDeconstructStep deconstructionOpt, BoundStatement body, bool @checked, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) : this(syntax, enumeratorInfoOpt, elementConversion, iterationVariableType, iterationVariables, iterationErrorExpressionOpt : null, expression, deconstructionOpt, body, @checked, breakLabel, continueLabel, hasErrors) { }
public BoundExpression VisitDynamicInvocation(BoundDynamicInvocation node, bool resultDiscarded) { var loweredArguments = VisitList(node.Arguments); bool hasImplicitReceiver; BoundExpression loweredReceiver; ImmutableArray <TypeSymbol> typeArguments; string name; switch (node.Expression.Kind) { case BoundKind.MethodGroup: // method invocation BoundMethodGroup methodGroup = (BoundMethodGroup)node.Expression; typeArguments = methodGroup.TypeArgumentsOpt; name = methodGroup.Name; hasImplicitReceiver = (methodGroup.Flags & BoundMethodGroupFlags.HasImplicitReceiver) != 0; // Should have been eliminated during binding of dynamic invocation: Debug.Assert(methodGroup.ReceiverOpt == null || methodGroup.ReceiverOpt.Kind != BoundKind.TypeOrValueExpression); if (methodGroup.ReceiverOpt == null) { // Calling a static method defined on an outer class via its simple name. NamedTypeSymbol firstContainer = node.ApplicableMethods.First().ContainingType; Debug.Assert(node.ApplicableMethods.All(m => m.IsStatic && m.ContainingType == firstContainer)); loweredReceiver = new BoundTypeExpression(node.Syntax, null, firstContainer); } else if (hasImplicitReceiver && factory.TopLevelMethod.IsStatic) { // Calling a static method defined on the current class via its simple name. loweredReceiver = new BoundTypeExpression(node.Syntax, null, factory.CurrentClass); } else { loweredReceiver = VisitExpression(methodGroup.ReceiverOpt); } // If we are calling a method on a NoPIA type, we need to embed all methods/properties // with the matching name of this dynamic invocation. EmbedIfNeedTo(loweredReceiver, methodGroup.Methods, node.Syntax); break; case BoundKind.DynamicMemberAccess: // method invocation var memberAccess = (BoundDynamicMemberAccess)node.Expression; name = memberAccess.Name; typeArguments = memberAccess.TypeArgumentsOpt; loweredReceiver = VisitExpression(memberAccess.Receiver); hasImplicitReceiver = false; break; default: // delegate invocation var loweredExpression = VisitExpression(node.Expression); return(dynamicFactory.MakeDynamicInvocation(loweredExpression, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, resultDiscarded).ToExpression()); } Debug.Assert(loweredReceiver != null); return(dynamicFactory.MakeDynamicMemberInvocation( name, loweredReceiver, typeArguments, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, hasImplicitReceiver, resultDiscarded).ToExpression()); }