Skip to content

Commit 388d835

Browse files
author
msftbot[bot]
authored
Merge pull request #41340 from 333fred/emit-headers
Support emitting function pointers in signatures
2 parents 9aab74d + 152ec2d commit 388d835

28 files changed

+984
-168
lines changed

src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS
434434

435435
case SyntaxKind.FunctionPointerType:
436436
return TypeWithAnnotations.Create(
437-
FunctionPointerTypeSymbol.CreateFunctionPointerTypeSymbolFromSource(
437+
FunctionPointerTypeSymbol.CreateFromSource(
438438
(FunctionPointerTypeSyntax)syntax,
439439
this,
440440
diagnostics,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
#nullable enable
3+
4+
using System.Collections.Generic;
5+
using System.Reflection.Metadata;
6+
using Microsoft.Cci;
7+
using Microsoft.CodeAnalysis.Emit;
8+
using Roslyn.Utilities;
9+
10+
namespace Microsoft.CodeAnalysis.CSharp.Symbols
11+
{
12+
internal sealed partial class FunctionPointerTypeSymbol : IFunctionPointerTypeReference
13+
{
14+
ISignature IFunctionPointerTypeReference.Signature => Signature;
15+
void IReference.Dispatch(MetadataVisitor visitor) => visitor.Visit((IFunctionPointerTypeReference)this);
16+
17+
bool ITypeReference.IsEnum => false;
18+
Cci.PrimitiveTypeCode ITypeReference.TypeCode => Cci.PrimitiveTypeCode.FunctionPointer;
19+
TypeDefinitionHandle ITypeReference.TypeDef => default;
20+
IGenericMethodParameterReference? ITypeReference.AsGenericMethodParameterReference => null;
21+
IGenericTypeInstanceReference? ITypeReference.AsGenericTypeInstanceReference => null;
22+
IGenericTypeParameterReference? ITypeReference.AsGenericTypeParameterReference => null;
23+
INamespaceTypeReference? ITypeReference.AsNamespaceTypeReference => null;
24+
INestedTypeReference? ITypeReference.AsNestedTypeReference => null;
25+
ISpecializedNestedTypeReference? ITypeReference.AsSpecializedNestedTypeReference => null;
26+
INamespaceTypeDefinition? ITypeReference.AsNamespaceTypeDefinition(EmitContext context) => null;
27+
INestedTypeDefinition? ITypeReference.AsNestedTypeDefinition(EmitContext context) => null;
28+
ITypeDefinition? ITypeReference.AsTypeDefinition(EmitContext context) => null;
29+
ITypeDefinition? ITypeReference.GetResolvedType(EmitContext context) => null;
30+
IEnumerable<ICustomAttribute> IReference.GetAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable<ICustomAttribute>();
31+
IDefinition? IReference.AsDefinition(EmitContext context) => null;
32+
}
33+
}

src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,9 @@ internal sealed override Cci.ITypeReference Translate(
986986

987987
case SymbolKind.TypeParameter:
988988
return Translate((TypeParameterSymbol)typeSymbol);
989+
990+
case SymbolKind.FunctionPointer:
991+
return Translate((FunctionPointerTypeSymbol)typeSymbol);
989992
}
990993

991994
throw ExceptionUtilities.UnexpectedValue(typeSymbol.Kind);
@@ -1347,6 +1350,11 @@ internal static Cci.IPointerTypeReference Translate(PointerTypeSymbol symbol)
13471350
return symbol;
13481351
}
13491352

1353+
internal static Cci.IFunctionPointerTypeReference Translate(FunctionPointerTypeSymbol symbol)
1354+
{
1355+
return symbol;
1356+
}
1357+
13501358
/// <summary>
13511359
/// Set the underlying implementation type for a given fixed-size buffer field.
13521360
/// </summary>

src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,27 @@ public override void VisitPointerType(IPointerTypeSymbol symbol)
147147
AddPunctuation(SyntaxKind.AsteriskToken);
148148
}
149149

150+
// PROTOTYPE(func-ptr): test
151+
public override void VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol)
152+
{
153+
AddKeyword(SyntaxKind.DelegateKeyword);
154+
AddPunctuation(SyntaxKind.AsteriskToken);
155+
156+
// PROTOTYPE(func-ptr): Expose calling convention and format here
157+
158+
AddPunctuation(SyntaxKind.LessThanToken);
159+
160+
symbol.Signature.ReturnType.Accept(this);
161+
162+
foreach (var param in symbol.Signature.Parameters)
163+
{
164+
AddPunctuation(SyntaxKind.CommaToken);
165+
param.Accept(this);
166+
}
167+
168+
AddPunctuation(SyntaxKind.GreaterThanToken);
169+
}
170+
150171
public override void VisitTypeParameter(ITypeParameterSymbol symbol)
151172
{
152173
if (this.isFirstSymbolVisited)

src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
77
using System.Diagnostics;
8+
using System.Linq;
89
using System.Reflection;
910
using Microsoft.Cci;
1011
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using Microsoft.CodeAnalysis.PooledObjects;
1113
using Roslyn.Utilities;
1214

1315
namespace Microsoft.CodeAnalysis.CSharp.Symbols
@@ -16,7 +18,7 @@ internal sealed class FunctionPointerMethodSymbol : MethodSymbol
1618
{
1719
private readonly ImmutableArray<FunctionPointerParameterSymbol> _parameters;
1820

19-
public static FunctionPointerMethodSymbol CreateMethodFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, DiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
21+
public static FunctionPointerMethodSymbol CreateFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, DiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
2022
{
2123
var (callingConvention, conventionIsValid) = FunctionPointerTypeSymbol.GetCallingConvention(syntax.CallingConvention.Text);
2224
if (!conventionIsValid)
@@ -27,6 +29,7 @@ public static FunctionPointerMethodSymbol CreateMethodFromSource(FunctionPointer
2729

2830
RefKind refKind = RefKind.None;
2931
TypeWithAnnotations returnType;
32+
var refReadonlyModifiers = ImmutableArray<CustomModifier>.Empty;
3033

3134
if (syntax.Parameters.Count == 0)
3235
{
@@ -35,8 +38,6 @@ public static FunctionPointerMethodSymbol CreateMethodFromSource(FunctionPointer
3538
else
3639
{
3740
var returnTypeParameter = syntax.Parameters[^1];
38-
39-
4041
var modifiers = returnTypeParameter.Modifiers;
4142
for (int i = 0; i < modifiers.Count; i++)
4243
{
@@ -48,6 +49,7 @@ public static FunctionPointerMethodSymbol CreateMethodFromSource(FunctionPointer
4849
{
4950
i++;
5051
refKind = RefKind.RefReadOnly;
52+
refReadonlyModifiers = ParameterHelpers.CreateInModifiers(typeBinder, diagnostics, returnTypeParameter);
5153
}
5254
else
5355
{
@@ -81,21 +83,28 @@ public static FunctionPointerMethodSymbol CreateMethodFromSource(FunctionPointer
8183
callingConvention,
8284
refKind,
8385
returnType,
86+
refReadonlyModifiers,
8487
syntax,
8588
typeBinder,
8689
diagnostics,
8790
suppressUseSiteDiagnostics);
8891
}
8992

93+
public static FunctionPointerMethodSymbol CreateFromMetadata(CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes)
94+
=> new FunctionPointerMethodSymbol(callingConvention, retAndParamTypes);
95+
96+
9097
private FunctionPointerMethodSymbol(
9198
CallingConvention callingConvention,
9299
RefKind refKind,
93100
TypeWithAnnotations returnType,
101+
ImmutableArray<CustomModifier> refCustomModifiers,
94102
FunctionPointerTypeSyntax syntax,
95103
Binder typeBinder,
96104
DiagnosticBag diagnostics,
97105
bool suppressUseSiteDiagnostics)
98106
{
107+
RefCustomModifiers = refCustomModifiers;
99108
CallingConvention = callingConvention;
100109
RefKind = refKind;
101110
ReturnTypeWithAnnotations = returnType;
@@ -110,6 +119,54 @@ private FunctionPointerMethodSymbol(
110119
: ImmutableArray<FunctionPointerParameterSymbol>.Empty;
111120
}
112121

122+
private FunctionPointerMethodSymbol(CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes)
123+
{
124+
Debug.Assert(retAndParamTypes.Length > 0);
125+
126+
ParamInfo<TypeSymbol> retInfo = retAndParamTypes[0];
127+
var returnType = TypeWithAnnotations.Create(retInfo.Type, customModifiers: CSharpCustomModifier.Convert(retInfo.CustomModifiers));
128+
129+
RefCustomModifiers = CSharpCustomModifier.Convert(retInfo.RefCustomModifiers);
130+
CallingConvention = callingConvention;
131+
ReturnTypeWithAnnotations = returnType;
132+
RefKind = getRefKind(retInfo, RefCustomModifiers, RefKind.RefReadOnly);
133+
_parameters = makeParametersFromMetadata(retAndParamTypes.AsSpan()[1..], this);
134+
135+
static ImmutableArray<FunctionPointerParameterSymbol> makeParametersFromMetadata(ReadOnlySpan<ParamInfo<TypeSymbol>> parameterTypes, FunctionPointerMethodSymbol parent)
136+
{
137+
if (parameterTypes.Length > 0)
138+
{
139+
var paramsBuilder = ArrayBuilder<FunctionPointerParameterSymbol>.GetInstance(parameterTypes.Length);
140+
141+
for (int i = 0; i < parameterTypes.Length; i++)
142+
{
143+
ParamInfo<TypeSymbol> param = parameterTypes[i];
144+
var paramRefCustomMods = CSharpCustomModifier.Convert(param.RefCustomModifiers);
145+
var paramType = TypeWithAnnotations.Create(param.Type, customModifiers: CSharpCustomModifier.Convert(param.CustomModifiers));
146+
RefKind paramRefKind = getRefKind(param, paramRefCustomMods, RefKind.In);
147+
paramsBuilder.Add(new FunctionPointerParameterSymbol(paramType, paramRefKind, i, parent, paramRefCustomMods));
148+
}
149+
150+
return paramsBuilder.ToImmutableAndFree();
151+
}
152+
else
153+
{
154+
return ImmutableArray<FunctionPointerParameterSymbol>.Empty;
155+
}
156+
}
157+
158+
static RefKind getRefKind(ParamInfo<TypeSymbol> param, ImmutableArray<CustomModifier> paramRefCustomMods, RefKind hasInRefKind)
159+
{
160+
// PROTOTYPE(func-ptr): Need to encode out params as a custom modifier of some kind
161+
return param.IsByRef switch
162+
{
163+
false => RefKind.None,
164+
true when CustomModifierUtils.HasInAttributeModifier(paramRefCustomMods) => hasInRefKind,
165+
true => RefKind.Ref,
166+
};
167+
}
168+
}
169+
113170
public override bool Equals(Symbol other, TypeCompareKind compareKind)
114171
{
115172
if (!(other is FunctionPointerMethodSymbol method))
@@ -131,6 +188,8 @@ internal bool Equals(FunctionPointerMethodSymbol other, TypeCompareKind compareK
131188
private bool EqualsNoParameters(FunctionPointerMethodSymbol other, TypeCompareKind compareKind, IReadOnlyDictionary<TypeParameterSymbol, bool>? isValueTypeOverride)
132189
=> CallingConvention == other.CallingConvention
133190
&& RefKind == other.RefKind
191+
&& ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) != 0
192+
|| RefCustomModifiers.SequenceEqual(other.RefCustomModifiers))
134193
&& ReturnTypeWithAnnotations.Equals(other.ReturnTypeWithAnnotations, compareKind, isValueTypeOverride);
135194

136195
public override int GetHashCode()
@@ -152,8 +211,7 @@ internal int GetHashCodeNoParameters()
152211
public override TypeWithAnnotations ReturnTypeWithAnnotations { get; }
153212
public override ImmutableArray<ParameterSymbol> Parameters =>
154213
_parameters.Cast<FunctionPointerParameterSymbol, ParameterSymbol>();
155-
// PROTOTYPE(func-ptr): Implement custom modifiers
156-
public override ImmutableArray<CustomModifier> RefCustomModifiers => throw new NotImplementedException();
214+
public override ImmutableArray<CustomModifier> RefCustomModifiers { get; }
157215
public override MethodKind MethodKind => MethodKind.FunctionPointerSignature;
158216

159217
internal override DiagnosticInfo? GetUseSiteDiagnostic()

src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ internal bool Equals(FunctionPointerParameterSymbol other, TypeCompareKind compa
4848

4949
internal bool MethodEqualityChecks(FunctionPointerParameterSymbol other, TypeCompareKind compareKind, IReadOnlyDictionary<TypeParameterSymbol, bool>? isValueTypeOverride)
5050
=> RefKind == other.RefKind
51-
// PROTOTYPE(func-ptr): When we can read in arbitrary modifiers from metadata, test this flag
52-
&& ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) == 0
53-
? RefCustomModifiers.SequenceEqual(other.RefCustomModifiers)
54-
: true)
51+
&& ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) != 0
52+
|| RefCustomModifiers.SequenceEqual(other.RefCustomModifiers))
5553
&& TypeWithAnnotations.Equals(other.TypeWithAnnotations, compareKind, isValueTypeOverride);
5654

5755
public override int GetHashCode()

src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
7+
using System.Diagnostics;
78
using Microsoft.Cci;
89
using Microsoft.CodeAnalysis.CSharp.Syntax;
910
using Microsoft.CodeAnalysis.PooledObjects;
@@ -12,19 +13,21 @@
1213
namespace Microsoft.CodeAnalysis.CSharp.Symbols
1314
{
1415
// PROTOTYPE(func-ptr): Support generic substitution and retargeting
15-
internal sealed class FunctionPointerTypeSymbol : TypeSymbol
16+
// PROTOTYPE(func-ptr): Match msvc's emitting of custom modifiers for calling convention
17+
internal sealed partial class FunctionPointerTypeSymbol : TypeSymbol
1618
{
17-
public static FunctionPointerTypeSymbol CreateFunctionPointerTypeSymbolFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, DiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
18-
{
19-
20-
return new FunctionPointerTypeSymbol(
21-
FunctionPointerMethodSymbol.CreateMethodFromSource(
19+
public static FunctionPointerTypeSymbol CreateFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, DiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
20+
=> new FunctionPointerTypeSymbol(
21+
FunctionPointerMethodSymbol.CreateFromSource(
2222
syntax,
2323
typeBinder,
2424
diagnostics,
2525
basesBeingResolved,
2626
suppressUseSiteDiagnostics));
27-
}
27+
28+
public static FunctionPointerTypeSymbol CreateFromMetadata(Cci.CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes)
29+
=> new FunctionPointerTypeSymbol(
30+
FunctionPointerMethodSymbol.CreateFromMetadata(callingConvention, retAndParamTypes));
2831

2932
public static (CallingConvention Convention, bool IsValid) GetCallingConvention(string convention) =>
3033
convention switch
@@ -39,11 +42,10 @@ public static (CallingConvention Convention, bool IsValid) GetCallingConvention(
3942

4043
private FunctionPointerTypeSymbol(FunctionPointerMethodSymbol signature)
4144
{
42-
_signature = signature;
45+
Signature = signature;
4346
}
4447

45-
private readonly FunctionPointerMethodSymbol _signature;
46-
public MethodSymbol Signature => _signature;
48+
public FunctionPointerMethodSymbol Signature { get; }
4749

4850
public override bool IsReferenceType => false;
4951
public override bool IsValueType => true;
@@ -83,24 +85,23 @@ internal override bool Equals(TypeSymbol t2, TypeCompareKind compareKind, IReadO
8385
return false;
8486
}
8587

86-
return _signature.Equals(other._signature, compareKind, isValueTypeOverrideOpt);
88+
return Signature.Equals(other.Signature, compareKind, isValueTypeOverrideOpt);
8789
}
8890

8991
public override int GetHashCode()
9092
{
91-
return Hash.Combine(1, _signature.GetHashCode());
93+
return Hash.Combine(1, Signature.GetHashCode());
9294
}
9395

9496
protected override ISymbol CreateISymbol()
9597
{
96-
// PROTOTYPE(func-ptr): Implement
97-
throw new NotImplementedException();
98+
return new PublicModel.FunctionPointerTypeSymbol(this, DefaultNullableAnnotation);
9899
}
99100

100101
protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation)
101102
{
102-
// PROTOTYPE(func-ptr): Implement
103-
throw new NotImplementedException();
103+
Debug.Assert(nullableAnnotation != DefaultNullableAnnotation);
104+
return new PublicModel.FunctionPointerTypeSymbol(this, nullableAnnotation);
104105
}
105106

106107
internal override void AddNullableTransforms(ArrayBuilder<byte> transforms)
@@ -117,12 +118,12 @@ internal override bool ApplyNullableTransforms(byte defaultTransformFlag, Immuta
117118

118119
internal override DiagnosticInfo? GetUseSiteDiagnostic()
119120
{
120-
return _signature.GetUseSiteDiagnostic();
121+
return Signature.GetUseSiteDiagnostic();
121122
}
122123

123124
internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo? result, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
124125
{
125-
return _signature.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes);
126+
return Signature.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes);
126127
}
127128

128129
internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance)

src/Compilers/CSharp/Portable/Symbols/Metadata/PE/SymbolFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ internal override TypeSymbol MakePointerTypeSymbol(PEModuleSymbol moduleSymbol,
4242
return new PointerTypeSymbol(CreateType(type, customModifiers));
4343
}
4444

45+
internal override TypeSymbol MakeFunctionPointerTypeSymbol(Cci.CallingConvention callingConvention, ImmutableArray<ParamInfo<TypeSymbol>> retAndParamTypes)
46+
{
47+
return FunctionPointerTypeSymbol.CreateFromMetadata(callingConvention, retAndParamTypes);
48+
}
49+
4550
internal override TypeSymbol GetEnumUnderlyingType(PEModuleSymbol moduleSymbol, TypeSymbol type)
4651
{
4752
return type.GetEnumUnderlyingType();

src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
6060
/// </code>
6161
/// </example>
6262
/// </summary>
63+
// PROTOTYPE(func-ptr): Implement and test in function pointers. Also dynamic and nullable
6364
internal struct TupleTypeDecoder
6465
{
6566
private readonly ImmutableArray<string?> _elementNames;
@@ -162,9 +163,11 @@ private TypeSymbol DecodeType(TypeSymbol type)
162163
_foundUsableErrorType = true;
163164
}
164165
return type;
166+
165167
case SymbolKind.DynamicType:
166168
case SymbolKind.TypeParameter:
167169
case SymbolKind.PointerType:
170+
case SymbolKind.FunctionPointer:
168171
return type;
169172

170173
case SymbolKind.NamedType:

0 commit comments

Comments
 (0)