Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Function Pointer Syntax #45684

Merged
13 commits merged into from
Jul 10, 2020
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6250,6 +6250,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_AddressOfToNonFunctionPointer" xml:space="preserve">
<value>Cannot convert &amp;method group '{0}' to non-function pointer type '{1}'.</value>
</data>
<data name="ERR_CannotSpecifyManagedWithUnmanagedSpecifiers" xml:space="preserve">
<value>'managed' calling convention cannot be combined with unmanaged calling convention specifiers.</value>
</data>
<data name="ERR_FeatureNotAvailableInVersion9" xml:space="preserve">
<value>Feature '{0}' is not available in C# 9.0. Please use language version {1} or greater.</value>
</data>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,11 @@ internal enum ErrorCode
ERR_DoesNotOverrideBaseEquals = 8871,

#endregion diagnostics introduced for C# 9.0

// PROTOTYPE(func-ptr): Pack before merge

ERR_CannotSpecifyManagedWithUnmanagedSpecifiers = 9500,

// Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd)
}
}
28 changes: 27 additions & 1 deletion src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

961 changes: 893 additions & 68 deletions src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs

Large diffs are not rendered by default.

125 changes: 116 additions & 9 deletions src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs

Large diffs are not rendered by default.

348 changes: 315 additions & 33 deletions src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs

Large diffs are not rendered by default.

216 changes: 136 additions & 80 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,19 @@ internal sealed class FunctionPointerMethodSymbol : MethodSymbol

public static FunctionPointerMethodSymbol CreateFromSource(FunctionPointerTypeSyntax syntax, Binder typeBinder, DiagnosticBag diagnostics, ConsList<TypeSymbol> basesBeingResolved, bool suppressUseSiteDiagnostics)
{
var (callingConvention, conventionIsValid) = FunctionPointerTypeSymbol.GetCallingConvention(syntax.CallingConvention.Text);
if (!conventionIsValid)
{
// '{0}' is not a valid calling convention for a function pointer. Valid conventions are 'cdecl', 'managed', 'thiscall', and 'stdcall'.
diagnostics.Add(ErrorCode.ERR_InvalidFunctionPointerCallingConvention, syntax.CallingConvention.GetLocation(), syntax.CallingConvention.Text);
}
var callingConvention = getCallingConvention(syntax.CallingConvention, diagnostics);

RefKind refKind = RefKind.None;
TypeWithAnnotations returnType;
var refReadonlyModifiers = ImmutableArray<CustomModifier>.Empty;

if (syntax.Parameters.Count == 0)
if (syntax.ParameterList.Parameters.Count == 0)
{
returnType = TypeWithAnnotations.Create(typeBinder.CreateErrorType());
}
else
{
var returnTypeParameter = syntax.Parameters[^1];
var returnTypeParameter = syntax.ParameterList.Parameters[^1];
var modifiers = returnTypeParameter.Modifiers;
for (int i = 0; i < modifiers.Count; i++)
{
Expand Down Expand Up @@ -98,6 +93,37 @@ public static FunctionPointerMethodSymbol CreateFromSource(FunctionPointerTypeSy
typeBinder,
diagnostics,
suppressUseSiteDiagnostics);

// PROTOTYPE(func-ptr): Do this right. Just making compatible changes for now
static CallingConvention getCallingConvention(FunctionPointerCallingConventionSyntax? callingConventionSyntax, DiagnosticBag diagnostics)
{
switch (callingConventionSyntax)
{
case null:
return CallingConvention.Default;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.ManagedKeyword } }:
Debug.Assert(callingConventionSyntax.UnmanagedCallingConventionList is null || callingConventionSyntax.ContainsDiagnostics);
return CallingConvention.Default;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.UnmanagedKeyword }, UnmanagedCallingConventionList: { } specifiers } when isCallingConvention(specifiers, "Cdecl"):
333fred marked this conversation as resolved.
Show resolved Hide resolved
return CallingConvention.CDecl;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.UnmanagedKeyword }, UnmanagedCallingConventionList: { } specifiers } when isCallingConvention(specifiers, "Thiscall"):
return CallingConvention.ThisCall;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.UnmanagedKeyword }, UnmanagedCallingConventionList: { } specifiers } when isCallingConvention(specifiers, "Fastcall"):
return CallingConvention.FastCall;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.UnmanagedKeyword }, UnmanagedCallingConventionList: { } specifiers } when isCallingConvention(specifiers, "Stdcall"):
return CallingConvention.Standard;
case { ManagedSpecifier: { RawKind: (int)SyntaxKind.UnmanagedKeyword }, UnmanagedCallingConventionList: null }:
return CallingConvention.Unmanaged;

default:
// PROTOTYPE(func-ptr): Handle the possible null and actually construct the correct convention
diagnostics.Add(ErrorCode.ERR_InvalidFunctionPointerCallingConvention, callingConventionSyntax.GetLocation(), callingConventionSyntax.UnmanagedCallingConventionList!.CallingConventions[0].Name.Text);
return CallingConvention.Default;
}

static bool isCallingConvention(FunctionPointerUnmanagedCallingConventionSyntaxList specifiers, string expected)
=> specifiers.CallingConventions[0].Name.Text == expected;
333fred marked this conversation as resolved.
Show resolved Hide resolved
333fred marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// <summary>
Expand Down Expand Up @@ -322,11 +348,11 @@ private FunctionPointerMethodSymbol(
RefKind = refKind;
ReturnTypeWithAnnotations = returnType;

_parameters = syntax.Parameters.Count > 1
_parameters = syntax.ParameterList.Parameters.Count > 1
? ParameterHelpers.MakeFunctionPointerParameters(
typeBinder,
this,
syntax.Parameters,
syntax.ParameterList.Parameters,
diagnostics,
suppressUseSiteDiagnostics)
: ImmutableArray<FunctionPointerParameterSymbol>.Empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,6 @@ public FunctionPointerTypeSymbol SubstituteTypeSymbol(
ImmutableArray<ImmutableArray<CustomModifier>> paramRefCustomModifiers)
=> new FunctionPointerTypeSymbol(Signature.SubstituteParameterSymbols(substitutedReturnType, substitutedParameterTypes, refCustomModifiers, paramRefCustomModifiers));

public static (CallingConvention Convention, bool IsValid) GetCallingConvention(string convention) =>
convention switch
{
"" => (CallingConvention.Default, true),
"cdecl" => (CallingConvention.CDecl, true),
"managed" => (CallingConvention.Default, true),
"thiscall" => (CallingConvention.ThisCall, true),
"stdcall" => (CallingConvention.Standard, true),
_ => (CallingConvention.Default, false),
};

private FunctionPointerTypeSymbol(FunctionPointerMethodSymbol signature)
{
Signature = signature;
Expand Down
41 changes: 22 additions & 19 deletions src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static ImmutableArray<ParameterSymbol> MakeParameters(
bool allowThis,
bool addRefReadOnlyModifier)
{
return MakeParameters<ParameterSymbol, Symbol>(
return MakeParameters<ParameterSyntax, ParameterSymbol, Symbol>(
binder,
owner,
syntax.Parameters,
Expand Down Expand Up @@ -59,11 +59,11 @@ public static ImmutableArray<ParameterSymbol> MakeParameters(
public static ImmutableArray<FunctionPointerParameterSymbol> MakeFunctionPointerParameters(
Binder binder,
FunctionPointerMethodSymbol owner,
SeparatedSyntaxList<ParameterSyntax> parametersList,
SeparatedSyntaxList<FunctionPointerParameterSyntax> parametersList,
DiagnosticBag diagnostics,
bool suppressUseSiteDiagnostics)
{
return MakeParameters(
return MakeParameters<FunctionPointerParameterSyntax, FunctionPointerParameterSymbol, FunctionPointerMethodSymbol>(
binder,
owner,
parametersList,
Expand All @@ -75,7 +75,7 @@ public static ImmutableArray<FunctionPointerParameterSymbol> MakeFunctionPointer
suppressUseSiteDiagnostics,
parametersList.Count - 2,
parameterCreationFunc: (Binder binder, FunctionPointerMethodSymbol owner, TypeWithAnnotations parameterType,
ParameterSyntax syntax, RefKind refKind, int ordinal,
FunctionPointerParameterSyntax syntax, RefKind refKind, int ordinal,
SyntaxToken paramsKeyword, SyntaxToken thisKeyword, bool addRefReadOnlyModifier,
DiagnosticBag diagnostics) =>
{
Expand Down Expand Up @@ -104,19 +104,20 @@ public static ImmutableArray<FunctionPointerParameterSymbol> MakeFunctionPointer
parsingFunctionPointer: true);
}

private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSymbol, TOwningSymbol>(
private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSyntax, TParameterSymbol, TOwningSymbol>(
Binder binder,
TOwningSymbol owner,
SeparatedSyntaxList<ParameterSyntax> parametersList,
SeparatedSyntaxList<TParameterSyntax> parametersList,
out SyntaxToken arglistToken,
DiagnosticBag diagnostics,
bool allowRefOrOut,
bool allowThis,
bool addRefReadOnlyModifier,
bool suppressUseSiteDiagnostics,
int lastIndex,
Func<Binder, TOwningSymbol, TypeWithAnnotations, ParameterSyntax, RefKind, int, SyntaxToken, SyntaxToken, bool, DiagnosticBag, TParameterSymbol> parameterCreationFunc,
Func<Binder, TOwningSymbol, TypeWithAnnotations, TParameterSyntax, RefKind, int, SyntaxToken, SyntaxToken, bool, DiagnosticBag, TParameterSymbol> parameterCreationFunc,
bool parsingFunctionPointer = false)
where TParameterSyntax : BaseParameterSyntax
where TParameterSymbol : ParameterSymbol
where TOwningSymbol : Symbol
{
Expand All @@ -135,10 +136,11 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSymbol,

if (mustBeLastParameter == null)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
if (parameterSyntax.Modifiers.Any(SyntaxKind.ParamsKeyword) ||
parameterSyntax.Identifier.Kind() == SyntaxKind.ArgListKeyword)
if (parameterSyntax is ParameterSyntax concreteParam &&
(concreteParam.Modifiers.Any(SyntaxKind.ParamsKeyword) ||
concreteParam.Identifier.Kind() == SyntaxKind.ArgListKeyword))
{
mustBeLastParameter = parameterSyntax;
mustBeLastParameter = concreteParam;
}
}

Expand All @@ -150,9 +152,9 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSymbol,
diagnostics.Add(ErrorCode.ERR_ThisInBadContext, thisKeyword.GetLocation());
}

if (parameterSyntax.IsArgList)
if (parameterSyntax is ParameterSyntax { IsArgList: true, Identifier: var identifier })
{
arglistToken = parameterSyntax.Identifier;
arglistToken = identifier;
// The native compiler produces "Expected type" here, in the parser. Roslyn produces
// the somewhat more informative "arglist not valid" error.
if (paramsKeyword.Kind() != SyntaxKind.None
Expand All @@ -166,7 +168,7 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSymbol,
continue;
}

if (parameterSyntax.Default != null && firstDefault == -1)
if (parameterSyntax is ParameterSyntax { Default: { } } && firstDefault == -1)
{
firstDefault = parameterIndex;
}
Expand Down Expand Up @@ -277,8 +279,8 @@ internal static void EnsureNullableAttributeExists(CSharpCompilation compilation

private static Location GetParameterLocation(ParameterSymbol parameter) => parameter.GetNonNullSyntaxNode().Location;

private static void CheckParameterModifiers(
ParameterSyntax parameter, DiagnosticBag diagnostics, bool parsingFunctionPointerParams)
private static void CheckParameterModifiers<TParameterSyntax>(
TParameterSyntax parameter, DiagnosticBag diagnostics, bool parsingFunctionPointerParams) where TParameterSyntax : BaseParameterSyntax
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
var seenThis = false;
var seenRef = false;
Expand Down Expand Up @@ -420,17 +422,18 @@ private static void CheckParameterModifiers(
}
}

private static void ReportParameterErrors(
private static void ReportParameterErrors<TParameterSyntax>(
Symbol owner,
ParameterSyntax parameterSyntax,
TParameterSyntax parameterSyntax,
333fred marked this conversation as resolved.
Show resolved Hide resolved
ParameterSymbol parameter,
SyntaxToken thisKeyword,
SyntaxToken paramsKeyword,
int firstDefault,
DiagnosticBag diagnostics)
where TParameterSyntax : BaseParameterSyntax
{
int parameterIndex = parameter.Ordinal;
bool isDefault = parameterSyntax.Default != null;
bool isDefault = parameterSyntax is ParameterSyntax { Default: { } };

if (thisKeyword.Kind() == SyntaxKind.ThisKeyword && parameterIndex != 0)
{
Expand Down Expand Up @@ -458,7 +461,7 @@ private static void ReportParameterErrors(
else if (firstDefault != -1 && parameterIndex > firstDefault && !isDefault && !parameter.IsParams)
{
// error CS1737: Optional parameters must appear after all required parameters
Location loc = parameterSyntax.Identifier.GetNextToken(includeZeroWidth: true).GetLocation(); //could be missing
Location loc = ((ParameterSyntax)(BaseParameterSyntax)parameterSyntax).Identifier.GetNextToken(includeZeroWidth: true).GetLocation(); //could be missing
diagnostics.Add(ErrorCode.ERR_DefaultValueBeforeRequiredValue, loc);
}
else if (parameter.RefKind != RefKind.None &&
Expand Down
Loading