|
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.Cci;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class FunctionPointerMethodSymbol : MethodSymbol
{
private readonly ImmutableArray<FunctionPointerParameterSymbol> _parameters;
private ImmutableHashSet<CustomModifier>? _lazyCallingConventionModifiers;
public static FunctionPointerMethodSymbol CreateFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, BindingDiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
{
ArrayBuilder<CustomModifier> customModifiers = ArrayBuilder<CustomModifier>.GetInstance();
CallingConvention callingConvention = getCallingConvention(typeBinder.Compilation, syntax.CallingConvention, customModifiers, diagnostics);
RefKind refKind = RefKind.None;
TypeWithAnnotations returnType;
if (syntax.ParameterList.Parameters.Count == 0)
{
returnType = TypeWithAnnotations.Create(typeBinder.CreateErrorType());
}
else
{
FunctionPointerParameterSyntax? returnTypeParameter = syntax.ParameterList.Parameters[^1];
SyntaxTokenList modifiers = returnTypeParameter.Modifiers;
for (int i = 0; i < modifiers.Count; i++)
{
SyntaxToken modifier = modifiers[i];
switch (modifier.Kind())
{
case SyntaxKind.RefKeyword when refKind == RefKind.None:
if (modifiers.Count > i + 1 && modifiers[i + 1].Kind() == SyntaxKind.ReadOnlyKeyword)
{
i++;
refKind = RefKind.RefReadOnly;
customModifiers.AddRange(ParameterHelpers.CreateInModifiers(typeBinder, diagnostics, returnTypeParameter));
}
else
{
refKind = RefKind.Ref;
}
break;
case SyntaxKind.RefKeyword:
Debug.Assert(refKind != RefKind.None);
// A return type can only have one '{0}' modifier.
diagnostics.Add(ErrorCode.ERR_DupReturnTypeMod, modifier.GetLocation(), modifier.Text);
break;
default:
// '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
diagnostics.Add(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, modifier.GetLocation(), modifier.Text);
break;
}
}
returnType = typeBinder.BindType(returnTypeParameter.Type, diagnostics, basesBeingResolved, suppressUseSiteDiagnostics);
if (returnType.IsVoidType() && refKind != RefKind.None)
{
diagnostics.Add(ErrorCode.ERR_NoVoidHere, returnTypeParameter.Location);
}
else if (returnType.IsStatic)
{
diagnostics.Add(ErrorFacts.GetStaticClassReturnCode(useWarning: false), returnTypeParameter.Location, returnType);
}
else if (returnType.IsRestrictedType(ignoreSpanLikeTypes: true))
{
diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, returnTypeParameter.Location, returnType);
}
}
var refCustomModifiers = ImmutableArray<CustomModifier>.Empty;
if (refKind != RefKind.None)
{
refCustomModifiers = customModifiers.ToImmutableAndFree();
}
else
{
returnType = returnType.WithModifiers(customModifiers.ToImmutableAndFree());
}
return new FunctionPointerMethodSymbol(
callingConvention,
refKind,
returnType,
refCustomModifiers,
syntax,
typeBinder,
diagnostics,
suppressUseSiteDiagnostics,
useUpdatedEscapeRules: typeBinder.UseUpdatedEscapeRules);
static CallingConvention getCallingConvention(CSharpCompilation compilation, FunctionPointerCallingConventionSyntax? callingConventionSyntax, ArrayBuilder<CustomModifier> customModifiers, BindingDiagnosticBag diagnostics)
{
switch (callingConventionSyntax?.ManagedOrUnmanagedKeyword.Kind())
{
case null:
return CallingConvention.Default;
case SyntaxKind.ManagedKeyword:
// Possible if we get a node not constructed by the parser
if (callingConventionSyntax.UnmanagedCallingConventionList is object && !callingConventionSyntax.ContainsDiagnostics)
{
diagnostics.Add(ErrorCode.ERR_CannotSpecifyManagedWithUnmanagedSpecifiers, callingConventionSyntax.UnmanagedCallingConventionList.GetLocation());
}
return CallingConvention.Default;
case SyntaxKind.UnmanagedKeyword:
// From the function pointers spec:
// C# recognizes 4 special identifiers that map to specific existing unmanaged CallKinds from ECMA 335.
// In order for this mapping to occur, these identifiers must be specified on their own, with no other
// identifiers, and this requirement is encoded into the spec for unmanaged_calling_conventions. These
// identifiers are Cdecl, Thiscall, Stdcall, and Fastcall, which correspond to unmanaged cdecl,
// unmanaged thiscall, unmanaged stdcall, and unmanaged fastcall, respectively. If more than one identifier
// is specified, or the single identifier is not of the specially recognized identifiers, we perform special
// name lookup on the identifier with the following rules:
//
// * We prepend the identifier with the string CallConv
// * We look only at types defined in the System.Runtime.CompilerServices namespace.
// * We look only at types defined in the core library of the application, which is the library that defines
// System.Object and has no dependencies.
//
// If lookup succeeds on all of the identifiers specified in an unmanaged_calling_convention, we encode the
// CallKind as unmanaged, and encode each of the resolved types in the set of modopts at the beginning of
// the function pointer signature.
switch (callingConventionSyntax.UnmanagedCallingConventionList)
{
case null:
checkUnmanagedSupport(compilation, callingConventionSyntax.ManagedOrUnmanagedKeyword.GetLocation(), diagnostics);
return CallingConvention.Unmanaged;
case { CallingConventions: { Count: 1 } specifiers }:
return specifiers[0].Name switch
{
// Special identifiers cases
{ ValueText: "Cdecl" } => CallingConvention.CDecl,
{ ValueText: "Stdcall" } => CallingConvention.Standard,
{ ValueText: "Thiscall" } => CallingConvention.ThisCall,
{ ValueText: "Fastcall" } => CallingConvention.FastCall,
// Unknown identifier case
_ => handleSingleConvention(specifiers[0], compilation, customModifiers, diagnostics)
};
case { CallingConventions: { Count: 0 } } unmanagedList:
// Should never be possible from parser-constructed code (parser will always provide at least a missing identifier token),
// so diagnostic quality isn't hugely important
if (!unmanagedList.ContainsDiagnostics)
{
diagnostics.Add(ErrorCode.ERR_InvalidFunctionPointerCallingConvention, unmanagedList.OpenBracketToken.GetLocation(), "");
}
return CallingConvention.Default;
case { CallingConventions: var specifiers }:
// More than one identifier case
checkUnmanagedSupport(compilation, callingConventionSyntax.ManagedOrUnmanagedKeyword.GetLocation(), diagnostics);
foreach (FunctionPointerUnmanagedCallingConventionSyntax? specifier in specifiers)
{
CustomModifier? modifier = handleIndividualUnrecognizedSpecifier(specifier, compilation, diagnostics);
if (modifier is object)
{
customModifiers.Add(modifier);
}
}
return CallingConvention.Unmanaged;
}
case var unexpected:
throw ExceptionUtilities.UnexpectedValue(unexpected);
}
static CallingConvention handleSingleConvention(FunctionPointerUnmanagedCallingConventionSyntax specifier, CSharpCompilation compilation, ArrayBuilder<CustomModifier> customModifiers, BindingDiagnosticBag diagnostics)
{
checkUnmanagedSupport(compilation, specifier.GetLocation(), diagnostics);
CustomModifier? modifier = handleIndividualUnrecognizedSpecifier(specifier, compilation, diagnostics);
if (modifier is object)
{
customModifiers.Add(modifier);
}
return CallingConvention.Unmanaged;
}
static CustomModifier? handleIndividualUnrecognizedSpecifier(FunctionPointerUnmanagedCallingConventionSyntax specifier, CSharpCompilation compilation, BindingDiagnosticBag diagnostics)
{
string specifierText = specifier.Name.ValueText;
if (string.IsNullOrEmpty(specifierText))
{
return null;
}
string typeName = "CallConv" + specifierText;
var metadataName = MetadataTypeName.FromNamespaceAndTypeName("System.Runtime.CompilerServices", typeName, useCLSCompliantNameArityEncoding: true, forcedArity: 0);
NamedTypeSymbol? specifierType;
specifierType = compilation.Assembly.CorLibrary.LookupDeclaredTopLevelMetadataType(ref metadataName);
Debug.Assert(specifierType?.IsErrorType() != true);
Debug.Assert(specifierType is null || ReferenceEquals(specifierType.ContainingAssembly, compilation.Assembly.CorLibrary));
if (specifierType is null)
{
specifierType = new MissingMetadataTypeSymbol.TopLevel(compilation.Assembly.CorLibrary.Modules[0], ref metadataName, new CSDiagnosticInfo(ErrorCode.ERR_TypeNotFound, typeName));
}
else if (specifierType.DeclaredAccessibility != Accessibility.Public)
{
diagnostics.Add(ErrorCode.ERR_TypeMustBePublic, specifier.GetLocation(), specifierType);
}
diagnostics.Add(specifierType.GetUseSiteInfo(), specifier);
return CSharpCustomModifier.CreateOptional(specifierType);
}
static void checkUnmanagedSupport(CSharpCompilation compilation, Location errorLocation, BindingDiagnosticBag diagnostics)
{
if (!compilation.Assembly.RuntimeSupportsUnmanagedSignatureCallingConvention)
{
diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportUnmanagedDefaultCallConv, errorLocation);
}
}
}
}
/// <summary>
/// Creates a function pointer method symbol from individual parts. This method should only be used when diagnostics are not needed.
/// This should only be used from testing code.
/// </summary>
internal static FunctionPointerMethodSymbol CreateFromPartsForTest(
CallingConvention callingConvention,
TypeWithAnnotations returnType,
ImmutableArray<CustomModifier> refCustomModifiers,
RefKind returnRefKind,
ImmutableArray<TypeWithAnnotations> parameterTypes,
ImmutableArray<ImmutableArray<CustomModifier>> parameterRefCustomModifiers,
ImmutableArray<RefKind> parameterRefKinds,
CSharpCompilation compilation)
{
return new FunctionPointerMethodSymbol(
callingConvention,
returnRefKind,
returnType,
refCustomModifiers,
parameterTypes,
parameterRefCustomModifiers,
parameterRefKinds,
compilation);
}
/// <summary>
/// Creates a function pointer method symbol from individual parts. This method should only be used when diagnostics are not needed.
/// </summary>
internal static FunctionPointerMethodSymbol CreateFromParts(
CallingConvention callingConvention,
ImmutableArray<CustomModifier> callingConventionModifiers,
TypeWithAnnotations returnTypeWithAnnotations,
RefKind returnRefKind,
ImmutableArray<TypeWithAnnotations> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds,
CSharpCompilation compilation)
{
var modifiersBuilder = ArrayBuilder<CustomModifier>.GetInstance();
if (!callingConventionModifiers.IsDefaultOrEmpty)
{
Debug.Assert(callingConvention == CallingConvention.Unmanaged);
modifiersBuilder.AddRange(callingConventionModifiers);
}
ImmutableArray<CustomModifier> refCustomModifiers;
if (returnRefKind == RefKind.None)
{
refCustomModifiers = ImmutableArray<CustomModifier>.Empty;
returnTypeWithAnnotations = returnTypeWithAnnotations.WithModifiers(modifiersBuilder.ToImmutableAndFree());
}
else
{
if (GetCustomModifierForRefKind(returnRefKind, compilation) is CustomModifier modifier)
{
modifiersBuilder.Add(modifier);
}
refCustomModifiers = modifiersBuilder.ToImmutableAndFree();
}
return new FunctionPointerMethodSymbol(
callingConvention,
returnRefKind,
returnTypeWithAnnotations,
refCustomModifiers,
parameterTypes,
parameterRefCustomModifiers: default,
parameterRefKinds,
compilation);
}
private static CustomModifier? GetCustomModifierForRefKind(RefKind refKind, CSharpCompilation compilation)
{
Debug.Assert(refKind is RefKind.None or RefKind.In or RefKind.Ref or RefKind.Out);
var attributeType = refKind switch
{
RefKind.In => compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_InAttribute),
RefKind.Out => compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_OutAttribute),
_ => null
};
if (attributeType is null)
{
Debug.Assert(refKind != RefKind.Out && refKind != RefKind.In);
return null;
}
return CSharpCustomModifier.CreateRequired(attributeType);
}
public static FunctionPointerMethodSymbol CreateFromMetadata(ModuleSymbol containingModule, CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes)
=> new FunctionPointerMethodSymbol(callingConvention, retAndParamTypes, useUpdatedEscapeRules: containingModule.UseUpdatedEscapeRules);
public FunctionPointerMethodSymbol SubstituteParameterSymbols(
TypeWithAnnotations substitutedReturnType,
ImmutableArray<TypeWithAnnotations> substitutedParameterTypes,
ImmutableArray<CustomModifier> refCustomModifiers = default,
ImmutableArray<ImmutableArray<CustomModifier>> paramRefCustomModifiers = default)
=> new FunctionPointerMethodSymbol(
this.CallingConvention,
this.RefKind,
substitutedReturnType,
refCustomModifiers.IsDefault ? this.RefCustomModifiers : refCustomModifiers,
this.Parameters,
substitutedParameterTypes,
paramRefCustomModifiers,
this.UseUpdatedEscapeRules);
internal FunctionPointerMethodSymbol MergeEquivalentTypes(FunctionPointerMethodSymbol signature, VarianceKind variance)
{
Debug.Assert(RefKind == signature.RefKind);
var returnVariance = RefKind == RefKind.None ? variance : VarianceKind.None;
var mergedReturnType = ReturnTypeWithAnnotations.MergeEquivalentTypes(signature.ReturnTypeWithAnnotations, returnVariance);
var mergedParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
bool hasParamChanges = false;
if (_parameters.Length > 0)
{
var paramMergedTypesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(_parameters.Length);
for (int i = 0; i < _parameters.Length; i++)
{
var thisParam = _parameters[i];
var otherParam = signature._parameters[i];
Debug.Assert(thisParam.RefKind == otherParam.RefKind);
var paramVariance = (variance, thisParam.RefKind) switch
{
(VarianceKind.In, RefKind.None) => VarianceKind.Out,
(VarianceKind.Out, RefKind.None) => VarianceKind.In,
_ => VarianceKind.None,
};
var mergedParameterType = thisParam.TypeWithAnnotations.MergeEquivalentTypes(otherParam.TypeWithAnnotations, paramVariance);
paramMergedTypesBuilder.Add(mergedParameterType);
if (!mergedParameterType.IsSameAs(thisParam.TypeWithAnnotations))
{
hasParamChanges = true;
}
}
if (hasParamChanges)
{
mergedParameterTypes = paramMergedTypesBuilder.ToImmutableAndFree();
}
else
{
paramMergedTypesBuilder.Free();
mergedParameterTypes = ParameterTypesWithAnnotations;
}
}
if (hasParamChanges || !mergedReturnType.IsSameAs(ReturnTypeWithAnnotations))
{
return SubstituteParameterSymbols(mergedReturnType, mergedParameterTypes);
}
else
{
return this;
}
}
public FunctionPointerMethodSymbol SetNullabilityForReferenceTypes(Func<TypeWithAnnotations, TypeWithAnnotations> transform)
{
var transformedReturn = transform(ReturnTypeWithAnnotations);
var transformedParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
bool hasParamChanges = false;
if (_parameters.Length > 0)
{
var paramTypesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(_parameters.Length);
foreach (var param in _parameters)
{
var transformedType = transform(param.TypeWithAnnotations);
paramTypesBuilder.Add(transformedType);
if (!transformedType.IsSameAs(param.TypeWithAnnotations))
{
hasParamChanges = true;
}
}
if (hasParamChanges)
{
transformedParameterTypes = paramTypesBuilder.ToImmutableAndFree();
}
else
{
paramTypesBuilder.Free();
transformedParameterTypes = ParameterTypesWithAnnotations;
}
}
if (hasParamChanges || !transformedReturn.IsSameAs(ReturnTypeWithAnnotations))
{
return SubstituteParameterSymbols(transformedReturn, transformedParameterTypes);
}
else
{
return this;
}
}
private FunctionPointerMethodSymbol(
CallingConvention callingConvention,
RefKind refKind,
TypeWithAnnotations returnType,
ImmutableArray<CustomModifier> refCustomModifiers,
ImmutableArray<ParameterSymbol> originalParameters,
ImmutableArray<TypeWithAnnotations> substitutedParameterTypes,
ImmutableArray<ImmutableArray<CustomModifier>> substitutedRefCustomModifiers,
bool useUpdatedEscapeRules)
{
Debug.Assert(originalParameters.Length == substitutedParameterTypes.Length);
Debug.Assert(substitutedRefCustomModifiers.IsDefault || originalParameters.Length == substitutedRefCustomModifiers.Length);
RefCustomModifiers = refCustomModifiers;
CallingConvention = callingConvention;
RefKind = refKind;
ReturnTypeWithAnnotations = returnType;
UseUpdatedEscapeRules = useUpdatedEscapeRules;
if (originalParameters.Length > 0)
{
var paramsBuilder = ArrayBuilder<FunctionPointerParameterSymbol>.GetInstance(originalParameters.Length);
for (int i = 0; i < originalParameters.Length; i++)
{
var originalParam = originalParameters[i];
var substitutedType = substitutedParameterTypes[i];
var customModifiers = substitutedRefCustomModifiers.IsDefault ? originalParam.RefCustomModifiers : substitutedRefCustomModifiers[i];
paramsBuilder.Add(new FunctionPointerParameterSymbol(
substitutedType,
originalParam.RefKind,
originalParam.Ordinal,
containingSymbol: this,
customModifiers));
}
_parameters = paramsBuilder.ToImmutableAndFree();
}
else
{
_parameters = ImmutableArray<FunctionPointerParameterSymbol>.Empty;
}
}
/// <summary>
/// Creates a function pointer method symbol from individual parts. This method should only be used when diagnostics are not needed.
/// </summary>
private FunctionPointerMethodSymbol(
CallingConvention callingConvention,
RefKind refKind,
TypeWithAnnotations returnTypeWithAnnotations,
ImmutableArray<CustomModifier> refCustomModifiers,
ImmutableArray<TypeWithAnnotations> parameterTypes,
ImmutableArray<ImmutableArray<CustomModifier>> parameterRefCustomModifiers,
ImmutableArray<RefKind> parameterRefKinds,
CSharpCompilation compilation)
{
Debug.Assert(refKind != RefKind.Out);
Debug.Assert(refCustomModifiers.IsDefaultOrEmpty || refKind != RefKind.None);
Debug.Assert(parameterRefCustomModifiers.IsDefault || parameterRefCustomModifiers.Length == parameterTypes.Length);
RefCustomModifiers = refCustomModifiers.IsDefault ? getCustomModifierArrayForRefKind(refKind, compilation) : refCustomModifiers;
RefKind = refKind;
CallingConvention = callingConvention;
ReturnTypeWithAnnotations = returnTypeWithAnnotations;
UseUpdatedEscapeRules = compilation.SourceModule.UseUpdatedEscapeRules;
_parameters = parameterTypes.ZipAsArray(parameterRefKinds, (Method: this, Comp: compilation, ParamRefCustomModifiers: parameterRefCustomModifiers),
(type, refKind, i, arg) =>
{
var refCustomModifiers = arg.ParamRefCustomModifiers.IsDefault ? getCustomModifierArrayForRefKind(refKind, arg.Comp) : arg.ParamRefCustomModifiers[i];
Debug.Assert(refCustomModifiers.IsEmpty || refKind != RefKind.None);
return new FunctionPointerParameterSymbol(type, refKind, i, arg.Method, refCustomModifiers: refCustomModifiers);
});
static ImmutableArray<CustomModifier> getCustomModifierArrayForRefKind(RefKind refKind, CSharpCompilation compilation)
=> GetCustomModifierForRefKind(refKind, compilation) is { } modifier ? ImmutableArray.Create(modifier) : ImmutableArray<CustomModifier>.Empty;
}
private FunctionPointerMethodSymbol(
CallingConvention callingConvention,
RefKind refKind,
TypeWithAnnotations returnType,
ImmutableArray<CustomModifier> refCustomModifiers,
FunctionPointerTypeSyntax syntax,
Binder typeBinder,
BindingDiagnosticBag diagnostics,
bool suppressUseSiteDiagnostics,
bool useUpdatedEscapeRules)
{
RefCustomModifiers = refCustomModifiers;
CallingConvention = callingConvention;
RefKind = refKind;
ReturnTypeWithAnnotations = returnType;
UseUpdatedEscapeRules = useUpdatedEscapeRules;
_parameters = syntax.ParameterList.Parameters.Count > 1
? ParameterHelpers.MakeFunctionPointerParameters(
typeBinder,
this,
syntax.ParameterList.Parameters,
diagnostics,
suppressUseSiteDiagnostics)
: ImmutableArray<FunctionPointerParameterSymbol>.Empty;
}
private FunctionPointerMethodSymbol(CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes, bool useUpdatedEscapeRules)
{
Debug.Assert(retAndParamTypes.Length > 0);
ParamInfo<TypeSymbol> retInfo = retAndParamTypes[0];
var returnType = TypeWithAnnotations.Create(retInfo.Type, customModifiers: CSharpCustomModifier.Convert(retInfo.CustomModifiers));
RefCustomModifiers = CSharpCustomModifier.Convert(retInfo.RefCustomModifiers);
CallingConvention = callingConvention;
ReturnTypeWithAnnotations = returnType;
RefKind = getRefKind(retInfo, RefCustomModifiers, RefKind.RefReadOnly, RefKind.Ref, requiresLocationAllowed: false);
Debug.Assert(RefKind != RefKind.Out);
UseUpdatedEscapeRules = useUpdatedEscapeRules;
_parameters = makeParametersFromMetadata(retAndParamTypes.AsSpan()[1..], this);
static ImmutableArray<FunctionPointerParameterSymbol> makeParametersFromMetadata(ReadOnlySpan<ParamInfo<TypeSymbol>> parameterTypes, FunctionPointerMethodSymbol parent)
{
if (parameterTypes.Length > 0)
{
var paramsBuilder = ArrayBuilder<FunctionPointerParameterSymbol>.GetInstance(parameterTypes.Length);
for (int i = 0; i < parameterTypes.Length; i++)
{
ParamInfo<TypeSymbol> param = parameterTypes[i];
var paramRefCustomMods = CSharpCustomModifier.Convert(param.RefCustomModifiers);
var paramType = TypeWithAnnotations.Create(param.Type, customModifiers: CSharpCustomModifier.Convert(param.CustomModifiers));
RefKind paramRefKind = getRefKind(param, paramRefCustomMods, RefKind.In, RefKind.Out, requiresLocationAllowed: true);
paramsBuilder.Add(new FunctionPointerParameterSymbol(paramType, paramRefKind, i, parent, paramRefCustomMods));
}
return paramsBuilder.ToImmutableAndFree();
}
else
{
return ImmutableArray<FunctionPointerParameterSymbol>.Empty;
}
}
static RefKind getRefKind(ParamInfo<TypeSymbol> param, ImmutableArray<CustomModifier> paramRefCustomMods, RefKind hasInRefKind, RefKind hasOutRefKind, bool requiresLocationAllowed)
{
return param.IsByRef switch
{
false => RefKind.None,
true when CustomModifierUtils.HasInAttributeModifier(paramRefCustomMods) => hasInRefKind,
true when CustomModifierUtils.HasOutAttributeModifier(paramRefCustomMods) => hasOutRefKind,
true when requiresLocationAllowed && CustomModifierUtils.HasRequiresLocationAttributeModifier(paramRefCustomMods) => RefKind.RefReadOnlyParameter,
true => RefKind.Ref,
};
}
}
internal void AddNullableTransforms(ArrayBuilder<byte> transforms)
{
ReturnTypeWithAnnotations.AddNullableTransforms(transforms);
foreach (var param in Parameters)
{
param.TypeWithAnnotations.AddNullableTransforms(transforms);
}
}
internal FunctionPointerMethodSymbol ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray<byte> transforms, ref int position)
{
bool madeChanges = ReturnTypeWithAnnotations.ApplyNullableTransforms(defaultTransformFlag, transforms, ref position, out var newReturnType);
var newParamTypes = ImmutableArray<TypeWithAnnotations>.Empty;
if (!Parameters.IsEmpty)
{
var paramTypesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(Parameters.Length);
bool madeParamChanges = false;
foreach (var param in Parameters)
{
madeParamChanges |= param.TypeWithAnnotations.ApplyNullableTransforms(defaultTransformFlag, transforms, ref position, out var newParamType);
paramTypesBuilder.Add(newParamType);
}
if (madeParamChanges)
{
newParamTypes = paramTypesBuilder.ToImmutableAndFree();
madeChanges = true;
}
else
{
paramTypesBuilder.Free();
newParamTypes = ParameterTypesWithAnnotations;
}
}
if (madeChanges)
{
return SubstituteParameterSymbols(newReturnType, newParamTypes);
}
else
{
return this;
}
}
internal override ImmutableArray<NamedTypeSymbol> UnmanagedCallingConventionTypes
{
get
{
if (!CallingConvention.IsCallingConvention(CallingConvention.Unmanaged))
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
var modifiersToSearch = RefKind != RefKind.None ? RefCustomModifiers : ReturnTypeWithAnnotations.CustomModifiers;
if (modifiersToSearch.IsEmpty)
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
var builder = ArrayBuilder<NamedTypeSymbol>.GetInstance(modifiersToSearch.Length);
foreach (CSharpCustomModifier modifier in modifiersToSearch)
{
if (FunctionPointerTypeSymbol.IsCallingConventionModifier(modifier.ModifierSymbol))
{
builder.Add(modifier.ModifierSymbol);
}
}
return builder.ToImmutableAndFree();
}
}
public ImmutableHashSet<CustomModifier> GetCallingConventionModifiers()
{
if (_lazyCallingConventionModifiers is null)
{
var modifiersToSearch = RefKind != RefKind.None ? RefCustomModifiers : ReturnTypeWithAnnotations.CustomModifiers;
if (modifiersToSearch.IsEmpty || CallingConvention != CallingConvention.Unmanaged)
{
_lazyCallingConventionModifiers = ImmutableHashSet<CustomModifier>.Empty;
}
else
{
var builder = PooledHashSet<CustomModifier>.GetInstance();
foreach (var modifier in modifiersToSearch)
{
if (FunctionPointerTypeSymbol.IsCallingConventionModifier(((CSharpCustomModifier)modifier).ModifierSymbol))
{
builder.Add(modifier);
}
}
if (builder.Count == 0)
{
_lazyCallingConventionModifiers = ImmutableHashSet<CustomModifier>.Empty;
}
else
{
_lazyCallingConventionModifiers = builder.ToImmutableHashSet();
}
builder.Free();
}
}
return _lazyCallingConventionModifiers;
}
public override bool Equals(Symbol other, TypeCompareKind compareKind)
{
if (!(other is FunctionPointerMethodSymbol method))
{
return false;
}
return Equals(method, compareKind);
}
internal bool Equals(FunctionPointerMethodSymbol other, TypeCompareKind compareKind)
{
return ReferenceEquals(this, other) ||
(EqualsNoParameters(other, compareKind)
&& _parameters.SequenceEqual(other._parameters, compareKind,
(param1, param2, compareKind) => param1.MethodEqualityChecks(param2, compareKind)));
}
private bool EqualsNoParameters(FunctionPointerMethodSymbol other, TypeCompareKind compareKind)
{
if (CallingConvention != other.CallingConvention
|| !FunctionPointerTypeSymbol.RefKindEquals(compareKind, RefKind, other.RefKind)
|| !ReturnTypeWithAnnotations.Equals(other.ReturnTypeWithAnnotations, compareKind))
{
return false;
}
// Calling convention modifiers are considered part of the equality of the function, even if the ignore
// custom modifiers bit is set. If the bit is not set, then no need to do anything as it will be compared
// with the rest of the modifiers. Order is significant in metadata, but at the type level ordering/duplication
// is not significant for these modifiers
if ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) != 0)
{
if (CallingConvention.IsCallingConvention(CallingConvention.Unmanaged)
&& !GetCallingConventionModifiers().SetEqualsWithoutIntermediateHashSet(other.GetCallingConventionModifiers()))
{
return false;
}
}
else if (!RefCustomModifiers.SequenceEqual(other.RefCustomModifiers))
{
return false;
}
return true;
}
public override int GetHashCode()
{
var currentHash = GetHashCodeNoParameters();
foreach (var param in _parameters)
{
currentHash = Hash.Combine(param.MethodHashCode(), currentHash);
}
return currentHash;
}
internal int GetHashCodeNoParameters()
=> Hash.Combine(ReturnType, Hash.Combine(((int)CallingConvention).GetHashCode(), ((int)FunctionPointerTypeSymbol.GetRefKindForHashCode(RefKind)).GetHashCode()));
internal override CallingConvention CallingConvention { get; }
internal override bool UseUpdatedEscapeRules { get; }
public override bool ReturnsVoid => ReturnTypeWithAnnotations.IsVoidType();
public override RefKind RefKind { get; }
public override TypeWithAnnotations ReturnTypeWithAnnotations { get; }
public override ImmutableArray<ParameterSymbol> Parameters =>
_parameters.Cast<FunctionPointerParameterSymbol, ParameterSymbol>();
public override ImmutableArray<CustomModifier> RefCustomModifiers { get; }
public override MethodKind MethodKind => MethodKind.FunctionPointerSignature;
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> info = default;
CalculateUseSiteDiagnostic(ref info);
if (CallingConvention.IsCallingConvention(CallingConvention.ExtraArguments))
{
MergeUseSiteInfo(ref info, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_UnsupportedCallingConvention, this)));
}
// Check for `modopt(RequiresLocation)` combined with any required modifier.
if (info.DiagnosticInfo?.Severity != DiagnosticSeverity.Error)
{
foreach (var parameter in this.Parameters)
{
if (CustomModifierUtils.HasRequiresLocationAttributeModifier(parameter.RefCustomModifiers) &&
parameter.RefCustomModifiers.Any(static m => !m.IsOptional))
{
MergeUseSiteInfo(ref info, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}
}
}
return info;
}
internal bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo? result, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
{
return ReturnType.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes)
|| GetUnificationUseSiteDiagnosticRecursive(ref result, RefCustomModifiers, owner, ref checkedTypes)
|| GetUnificationUseSiteDiagnosticRecursive(ref result, Parameters, owner, ref checkedTypes);
}
public override bool IsVararg
{
get
{
var isVararg = CallingConvention.IsCallingConvention(CallingConvention.ExtraArguments);
Debug.Assert(!isVararg || HasUseSiteError);
return isVararg;
}
}
public override Symbol? ContainingSymbol => null;
// Function pointers cannot have type parameters
public override int Arity => 0;
public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty;
public override bool IsExtensionMethod => false;
public override bool HidesBaseMethodsByName => false;
public override bool IsAsync => false;
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty;
public override Symbol? AssociatedSymbol => null;
public override ImmutableArray<Location> Locations => ImmutableArray<Location>.Empty;
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ImmutableArray<SyntaxReference>.Empty;
public override Accessibility DeclaredAccessibility => Accessibility.NotApplicable;
public override bool IsStatic => false;
public override bool IsVirtual => false;
public override bool IsOverride => false;
public override bool IsAbstract => false;
public override bool IsSealed => false;
public override bool IsExtern => false;
public override bool IsImplicitlyDeclared => true;
public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty;
internal override bool HasSpecialName => false;
internal override MethodImplAttributes ImplementationAttributes => default;
internal override bool HasDeclarativeSecurity => false;
internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null;
internal override bool RequiresSecurityObject => false;
internal override bool IsDeclaredReadOnly => false;
internal override bool IsInitOnly => false;
internal override ImmutableArray<string> GetAppliedConditionalSymbols() => ImmutableArray<string>.Empty;
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull => ImmutableHashSet<string>.Empty;
public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false;
internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => false;
internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null;
internal override bool GenerateDebugInfo => throw ExceptionUtilities.Unreachable();
internal override ObsoleteAttributeData? ObsoleteAttributeData => throw ExceptionUtilities.Unreachable();
public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable();
public override DllImportData GetDllImportData() => throw ExceptionUtilities.Unreachable();
internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => throw ExceptionUtilities.Unreachable();
internal override IEnumerable<SecurityAttribute> GetSecurityInformation() => throw ExceptionUtilities.Unreachable();
internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable();
protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable();
internal sealed override bool HasUnscopedRefAttribute => false;
internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
{
builderArgument = null;
return false;
}
internal sealed override int? TryGetOverloadResolutionPriority()
{
return null;
}
}
}
|