| File: CodeGen\CodeGenerator_RefSafety.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; namespace Microsoft.CodeAnalysis.CSharp.CodeGen; internal partial class CodeGenerator { /// <inheritdoc cref="MightEscapeTemporaryRefs(bool, TypeSymbol, RefKind, ParameterSymbol?, ImmutableArray{ParameterSymbol})"/> private static bool MightEscapeTemporaryRefs(BoundCall node, bool used) { return MightEscapeTemporaryRefs( used: used, returnType: node.Type, returnRefKind: node.Method.RefKind, thisParameterSymbol: node.Method.TryGetThisParameter(out var thisParameter) ? thisParameter : null, parameters: node.Method.Parameters); } /// <inheritdoc cref="MightEscapeTemporaryRefs(bool, TypeSymbol, RefKind, ParameterSymbol?, ImmutableArray{ParameterSymbol})"/> private static bool MightEscapeTemporaryRefs(BoundObjectCreationExpression node, bool used) { return MightEscapeTemporaryRefs( used: used, returnType: node.Type, returnRefKind: RefKind.None, thisParameterSymbol: null, parameters: node.Constructor.Parameters); } /// <inheritdoc cref="MightEscapeTemporaryRefs(bool, TypeSymbol, RefKind, ParameterSymbol?, ImmutableArray{ParameterSymbol})"/> private static bool MightEscapeTemporaryRefs(BoundFunctionPointerInvocation node, bool used) { FunctionPointerMethodSymbol method = node.FunctionPointer.Signature; return MightEscapeTemporaryRefs( used: used, returnType: node.Type, returnRefKind: method.RefKind, thisParameterSymbol: null, parameters: method.Parameters); } /// <summary> /// Determines whether a 'ref' can be captured by the call (considering only its signature). /// </summary> /// <remarks> /// <para> /// The emit layer consults this to avoid reusing temporaries that are passed by ref to such methods. /// </para> /// <para> /// This is a heuristic which might have false positives, i.e., /// it might not recognize that a call cannot capture a 'ref' /// even if the binding-time ref safety analysis would recognize that. /// That is fine, the IL will be correct, albeit less optimal (it might use more temps). /// But we still want some heuristic to avoid regressing IL of common safe cases. /// </para> /// </remarks> private static bool MightEscapeTemporaryRefs( bool used, TypeSymbol returnType, RefKind returnRefKind, ParameterSymbol? thisParameterSymbol, ImmutableArray<ParameterSymbol> parameters) { // whether we have any outputs that can capture `ref`s bool anyRefTargets = false; // whether we have any inputs that can contain `ref`s bool anyRefSources = false; if (used && (returnRefKind != RefKind.None || returnType.IsRefLikeOrAllowsRefLikeType())) { // If returning by ref or returning a ref struct, the result might capture `ref`s. anyRefTargets = true; } if (thisParameterSymbol is not null && processParameter(thisParameterSymbol, anyRefSources: ref anyRefSources, anyRefTargets: ref anyRefTargets)) { return true; } foreach (var parameter in parameters) { if (processParameter(parameter, anyRefSources: ref anyRefSources, anyRefTargets: ref anyRefTargets)) { return true; } } return false; // Returns true if we can return 'true' early. static bool processParameter(ParameterSymbol parameter, ref bool anyRefSources, ref bool anyRefTargets) { if (parameter.Type.IsRefLikeOrAllowsRefLikeType() && parameter.EffectiveScope != ScopedKind.ScopedValue) { anyRefSources = true; if (parameter.RefKind.IsWritableReference()) { anyRefTargets = true; } } else if (parameter.RefKind != RefKind.None && parameter.EffectiveScope == ScopedKind.None) { anyRefSources = true; } // If there is at least one output and at least one input, a `ref` can be captured. return anyRefTargets && anyRefSources; } } }