private TypeConversions( AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, TypeConversions otherNullabilityOpt ) : base(corLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { }
private void Validate() { if (this == Empty) { return; } DiagnosticBag semanticDiagnostics = _compilation.DeclarationDiagnostics; // Check constraints within named aliases. // Force resolution of named aliases. foreach (var(_, alias) in UsingAliases) { alias.Alias.GetAliasTarget(basesBeingResolved: null); semanticDiagnostics.AddRange(alias.Alias.AliasTargetDiagnostics); } foreach (var(_, alias) in UsingAliases) { alias.Alias.CheckConstraints(semanticDiagnostics); } var corLibrary = _compilation.SourceAssembly.CorLibrary; var conversions = new TypeConversions(corLibrary); foreach (var @using in Usings) { // Check if `using static` directives meet constraints. if (@using.NamespaceOrType.IsType) { var typeSymbol = (TypeSymbol)@using.NamespaceOrType; var location = @using.UsingDirective?.Name.Location ?? NoLocation.Singleton; typeSymbol.CheckAllConstraints(conversions, location, semanticDiagnostics); } } // Force resolution of extern aliases. foreach (var alias in ExternAliases) { alias.Alias.GetAliasTarget(null); semanticDiagnostics.AddRange(alias.Alias.AliasTargetDiagnostics); } if (_diagnostics != null && !_diagnostics.IsEmptyWithoutResolution) { semanticDiagnostics.AddRange(_diagnostics.AsEnumerable()); } }
/// <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; }
/// <summary> /// Lower a foreach loop that will enumerate a collection using an enumerator. /// /// E e = ((C)(x)).GetEnumerator() /// try { /// while (e.MoveNext()) { /// V v = (V)(T)e.Current; /// // body /// } /// } /// finally { /// // clean up e /// } /// </summary> private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt; Debug.Assert(enumeratorInfo != null); BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType; TypeSymbol elementType = enumeratorInfo.ElementType; // E e LocalSymbol enumeratorVar = _factory.SynthesizedLocal(enumeratorType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachEnumerator); // Reference to e. BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType); // ((C)(x)).GetEnumerator() or (x).GetEnumerator(); BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, enumeratorInfo.GetEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType); // E e = ((C)(x)).GetEnumerator(); BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue); AddForEachExpressionSequencePoint(forEachSyntax, ref enumeratorVarDecl); // V v LocalSymbol iterationVar = node.IterationVariable; //(V)(T)e.Current BoundExpression iterationVarAssignValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: MakeConversion( syntax: forEachSyntax, rewrittenOperand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.CurrentPropertyGetter), conversion: enumeratorInfo.CurrentConversion, rewrittenType: elementType, @checked: node.Checked), conversion: node.ElementConversion, rewrittenType: iterationVar.Type, @checked: node.Checked); // V v = (V)(T)e.Current; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarAssignValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* node.Body */ // } var rewrittenBodyBlock = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); BoundStatement whileLoop = RewriteWhileStatement( syntax: forEachSyntax, rewrittenCondition: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.MoveNextMethod), conditionSequencePointSpan: forEachSyntax.InKeyword.Span, rewrittenBody: rewrittenBodyBlock, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: false); BoundStatement result; MethodSymbol disposeMethod; if (enumeratorInfo.NeedsDisposeMethod && Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, forEachSyntax, _diagnostics, out disposeMethod)) { Binder.ReportDiagnosticsIfObsolete(_diagnostics, disposeMethod, forEachSyntax, hasBaseReceiver: false, containingMember: _factory.CurrentMethod, containingType: _factory.CurrentType, location: enumeratorInfo.Location); BoundBlock finallyBlockOpt; var idisposableTypeSymbol = disposeMethod.ContainingType; var conversions = new TypeConversions(_factory.CurrentMethod.ContainingAssembly.CorLibrary); HashSet <DiagnosticInfo> useSiteDiagnostics = null; var isImplicit = conversions.ClassifyImplicitConversion(enumeratorType, idisposableTypeSymbol, ref useSiteDiagnostics).IsImplicit; _diagnostics.Add(forEachSyntax, useSiteDiagnostics); if (isImplicit) { Debug.Assert(enumeratorInfo.NeedsDisposeMethod); Conversion receiverConversion = enumeratorType.IsStructType() ? Conversion.Boxing : Conversion.ImplicitReference; // ((IDisposable)e).Dispose(); or e.Dispose(); BoundStatement disposeCall = new BoundExpressionStatement(forEachSyntax, expression: SynthesizeCall(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol)); BoundStatement disposeStmt; if (enumeratorType.IsValueType) { // No way for the struct to be nullable and disposable. Debug.Assert(((TypeSymbol)enumeratorType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T); // For non-nullable structs, no null check is required. disposeStmt = disposeCall; } else { // NB: cast to object missing from spec. Needed to ignore user-defined operators and box type parameters. // if ((object)e != null) ((IDisposable)e).Dispose(); disposeStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, left: MakeConversion( syntax: forEachSyntax, rewrittenOperand: boundEnumeratorVar, conversion: enumeratorInfo.EnumeratorConversion, rewrittenType: _compilation.GetSpecialType(SpecialType.System_Object), @checked: false), right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: _compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: disposeCall, rewrittenAlternativeOpt: null, hasErrors: false); } finallyBlockOpt = new BoundBlock(forEachSyntax, locals: ImmutableArray <LocalSymbol> .Empty, localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(disposeStmt)); } else { Debug.Assert(!enumeratorType.IsSealed); // IDisposable d LocalSymbol disposableVar = _factory.SynthesizedLocal(idisposableTypeSymbol); // Reference to d. BoundLocal boundDisposableVar = MakeBoundLocal(forEachSyntax, disposableVar, idisposableTypeSymbol); BoundTypeExpression boundIDisposableTypeExpr = new BoundTypeExpression(forEachSyntax, aliasOpt: null, type: idisposableTypeSymbol); // e as IDisposable BoundExpression disposableVarInitValue = new BoundAsOperator(forEachSyntax, operand: boundEnumeratorVar, targetType: boundIDisposableTypeExpr, conversion: Conversion.ExplicitReference, // Explicit so the emitter won't optimize it away. type: idisposableTypeSymbol); // IDisposable d = e as IDisposable; BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue); // if (d != null) d.Dispose(); BoundStatement ifStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, // reference equality left: boundDisposableVar, right: MakeLiteral(forEachSyntax, constantValue: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: _compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: new BoundExpressionStatement(forEachSyntax, expression: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundDisposableVar, method: disposeMethod)), rewrittenAlternativeOpt: null, hasErrors: false); // IDisposable d = e as IDisposable; // if (d != null) d.Dispose(); finallyBlockOpt = new BoundBlock(forEachSyntax, locals: ImmutableArray.Create <LocalSymbol>(disposableVar), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(disposableVarDecl, ifStmt)); } // try { // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } // } // finally { // /* dispose of e */ // } BoundStatement tryFinally = new BoundTryStatement(forEachSyntax, tryBlock: new BoundBlock(forEachSyntax, locals: ImmutableArray <LocalSymbol> .Empty, localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(whileLoop)), catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: finallyBlockOpt); // E e = ((C)(x)).GetEnumerator(); // try { // /* as above */ result = new BoundBlock( syntax: forEachSyntax, locals: ImmutableArray.Create(enumeratorVar), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, tryFinally)); } else { // E e = ((C)(x)).GetEnumerator(); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } result = new BoundBlock( syntax: forEachSyntax, locals: ImmutableArray.Create(enumeratorVar), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, whileLoop)); } AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
private void Validate() { if (this == Empty) { return; } DiagnosticBag semanticDiagnostics = _compilation.DeclarationDiagnostics; // Check constraints within named aliases. var diagnostics = BindingDiagnosticBag.GetInstance(); // Force resolution of named aliases. foreach (var(_, alias) in UsingAliases) { NamespaceOrTypeSymbol target = alias.Alias.GetAliasTarget(basesBeingResolved: null); diagnostics.Clear(); diagnostics.AddRange(alias.Alias.AliasTargetDiagnostics); alias.Alias.CheckConstraints(diagnostics); semanticDiagnostics.AddRange(diagnostics.DiagnosticBag); recordImportDependencies(alias.UsingDirective, target); } var corLibrary = _compilation.SourceAssembly.CorLibrary; var conversions = new TypeConversions(corLibrary); foreach (var @using in Usings) { diagnostics.Clear(); diagnostics.AddDependencies(@using.Dependencies); NamespaceOrTypeSymbol target = @using.NamespaceOrType; // Check if `using static` directives meet constraints. UsingDirectiveSyntax usingDirective = @using.UsingDirective; if (target.IsType) { var typeSymbol = (TypeSymbol)target; var location = usingDirective?.Name.Location ?? NoLocation.Singleton; typeSymbol.CheckAllConstraints(_compilation, conversions, location, diagnostics); } semanticDiagnostics.AddRange(diagnostics.DiagnosticBag); recordImportDependencies(usingDirective, target); } // Force resolution of extern aliases. foreach (var alias in ExternAliases) { var target = (NamespaceSymbol)alias.Alias.GetAliasTarget(null); Debug.Assert(target.IsGlobalNamespace); semanticDiagnostics.AddRange(alias.Alias.AliasTargetDiagnostics.DiagnosticBag); if (!Compilation.ReportUnusedImportsInTree(alias.ExternAliasDirective.SyntaxTree)) { diagnostics.Clear(); diagnostics.AddAssembliesUsedByNamespaceReference(target); _compilation.AddUsedAssemblies(diagnostics.DependenciesBag); } } if (_diagnostics != null && !_diagnostics.IsEmptyWithoutResolution) { semanticDiagnostics.AddRange(_diagnostics.AsEnumerable()); } diagnostics.Free(); void recordImportDependencies(UsingDirectiveSyntax usingDirective, NamespaceOrTypeSymbol target) { if (usingDirective is object && Compilation.ReportUnusedImportsInTree(usingDirective.SyntaxTree)) { _compilation.RecordImportDependencies(usingDirective, diagnostics.DependenciesBag.ToImmutableArray()); } else { if (target.IsNamespace) { diagnostics.AddAssembliesUsedByNamespaceReference((NamespaceSymbol)target); } _compilation.AddUsedAssemblies(diagnostics.DependenciesBag); } } }