Skip to content

Commit

Permalink
Infer delegate types with -langversion:preview only (#53241)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored May 12, 2021
1 parent 876bf34 commit 82ce937
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 91 deletions.
8 changes: 4 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,9 @@ private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, Bou
// UNDONE: is converted to a delegate that does not match. What to surface then?

var unboundLambda = (UnboundLambda)source;
if (destination.SpecialType == SpecialType.System_Delegate || destination.IsNonGenericExpressionType())
if ((destination.SpecialType == SpecialType.System_Delegate || destination.IsNonGenericExpressionType()) &&
syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType))
{
CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInferredDelegateType, diagnostics);
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var delegateType = unboundLambda.InferDelegateType(ref useSiteInfo);
BoundLambda boundLambda;
Expand Down Expand Up @@ -581,9 +581,9 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
hasErrors = true;
}

if (destination.SpecialType == SpecialType.System_Delegate)
if (destination.SpecialType == SpecialType.System_Delegate &&
syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType))
{
CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInferredDelegateType, diagnostics);
// https://github.com/dotnet/roslyn/issues/52869: Avoid calculating the delegate type multiple times during conversion.
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var delegateType = GetMethodGroupDelegateType(group, ref useSiteInfo);
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2264,7 +2264,8 @@ void reportMethodGroupErrors(BoundMethodGroup methodGroup, bool fromAddressOf)
{
errorCode = ErrorCode.ERR_AddressOfToNonFunctionPointer;
}
else if (targetType.SpecialType == SpecialType.System_Delegate)
else if (targetType.SpecialType == SpecialType.System_Delegate &&
syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType))
{
Error(diagnostics, ErrorCode.ERR_CannotInferDelegateType, location);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected override ConversionsBase WithNullabilityCore(bool includeNullability)
public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup source, TypeSymbol destination, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// Must be a bona fide delegate type, not an expression tree type.
if (!(destination.IsDelegateType() || destination.SpecialType == SpecialType.System_Delegate))
if (!(destination.IsDelegateType() || (destination.SpecialType == SpecialType.System_Delegate && IsFeatureInferredDelegateTypeEnabled(source))))
{
return Conversion.NoConversion;
}
Expand Down Expand Up @@ -124,7 +124,7 @@ private static MethodGroupResolution ResolveDelegateOrFunctionPointerMethodGroup
return (signature, true, new CallingConventionInfo(signature.CallingConvention, signature.GetCallingConventionModifiers()));
}

var delegateType = (type.SpecialType == SpecialType.System_Delegate) ?
var delegateType = (type.SpecialType == SpecialType.System_Delegate) && methodGroup.Syntax.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes) ?
// https://github.com/dotnet/roslyn/issues/52869: Avoid calculating the delegate type multiple times during conversion.
_binder.GetMethodGroupDelegateType(methodGroup, ref useSiteInfo) :
type.GetDelegateType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1400,20 +1400,31 @@ public static LambdaConversionResult IsAnonymousFunctionCompatibleWithType(Unbou

if (type.SpecialType == SpecialType.System_Delegate)
{
return LambdaConversionResult.Success;
if (IsFeatureInferredDelegateTypeEnabled(anonymousFunction))
{
return LambdaConversionResult.Success;
}
}
else if (type.IsDelegateType())
{
return IsAnonymousFunctionCompatibleWithDelegate(anonymousFunction, type);
}
else if (type.IsGenericOrNonGenericExpressionType(out bool _))
else if (type.IsGenericOrNonGenericExpressionType(out bool isGenericType))
{
return IsAnonymousFunctionCompatibleWithExpressionTree(anonymousFunction, (NamedTypeSymbol)type);
if (isGenericType || IsFeatureInferredDelegateTypeEnabled(anonymousFunction))
{
return IsAnonymousFunctionCompatibleWithExpressionTree(anonymousFunction, (NamedTypeSymbol)type);
}
}

return LambdaConversionResult.BadTargetType;
}

internal static bool IsFeatureInferredDelegateTypeEnabled(BoundExpression expr)
{
return expr.Syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType);
}

private static bool HasAnonymousFunctionConversion(BoundExpression source, TypeSymbol destination)
{
Debug.Assert(source != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Linq;

namespace Microsoft.CodeAnalysis.CSharp
Expand All @@ -12,7 +10,12 @@ internal static class CSharpCompilationExtensions
{
internal static bool IsFeatureEnabled(this CSharpCompilation compilation, MessageID feature)
{
return ((CSharpParseOptions)compilation.SyntaxTrees.FirstOrDefault()?.Options)?.IsFeatureEnabled(feature) == true;
return ((CSharpParseOptions?)compilation.SyntaxTrees.FirstOrDefault()?.Options)?.IsFeatureEnabled(feature) == true;
}

internal static bool IsFeatureEnabled(this SyntaxNode? syntax, MessageID feature)
{
return ((CSharpParseOptions?)syntax?.SyntaxTree.Options)?.IsFeatureEnabled(feature) == true;
}
}
}
64 changes: 29 additions & 35 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleEqualityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1358,12 +1358,9 @@ static void Main()
// (6,30): error CS0034: Operator '==' is ambiguous on operands of type '<null>' and 'default'
// System.Console.Write((null, () => 1) == (default, default));
Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "(null, () => 1) == (default, default)").WithArguments("==", "<null>", "default").WithLocation(6, 30),
// (6,37): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (6,30): error CS0019: Operator '==' cannot be applied to operands of type 'lambda expression' and 'default'
// System.Console.Write((null, () => 1) == (default, default));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "() => 1").WithArguments("inferred delegate type").WithLocation(6, 37),
// (6,37): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write((null, () => 1) == (default, default));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "() => 1").WithArguments("inferred delegate type").WithLocation(6, 37),
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, () => 1) == (default, default)").WithArguments("==", "lambda expression", "default").WithLocation(6, 30),
// (7,30): error CS0034: Operator '==' is ambiguous on operands of type '(<null>, lambda expression)' and 'default'
// System.Console.Write((null, () => 2) == default);
Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "(null, () => 2) == default").WithArguments("==", "(<null>, lambda expression)", "default").WithLocation(7, 30)
Expand Down Expand Up @@ -1662,31 +1659,16 @@ static void Main()
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9);
comp.VerifyDiagnostics(
// (6,65): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "x => x").WithArguments("inferred delegate type").WithLocation(6, 65),
// (6,65): error CS8917: The delegate type could not be inferred.
// (6,30): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'lambda expression'
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65),
// (6,65): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "<null>", "lambda expression").WithLocation(6, 30),
// (6,30): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'method group'
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "x => x").WithArguments("inferred delegate type").WithLocation(6, 65),
// (6,65): error CS8917: The delegate type could not be inferred.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65),
// (6,73): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "<null>", "method group").WithLocation(6, 30),
// (6,30): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'lambda expression'
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "Main").WithArguments("inferred delegate type").WithLocation(6, 73),
// (6,73): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "Main").WithArguments("inferred delegate type").WithLocation(6, 73),
// (6,79): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "(int i) => { int j = 0; return i + j; }").WithArguments("inferred delegate type").WithLocation(6, 79),
// (6,79): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_FeatureInPreview, "(int i) => { int j = 0; return i + j; }").WithArguments("inferred delegate type").WithLocation(6, 79));
verify(comp);
Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "<null>", "lambda expression").WithLocation(6, 30));
verify(comp, inferDelegate: false);

comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview);
comp.VerifyDiagnostics(
Expand All @@ -1696,9 +1678,9 @@ static void Main()
// (6,65): error CS8917: The delegate type could not be inferred.
// System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; }));
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65));
verify(comp);
verify(comp, inferDelegate: true);

static void verify(CSharpCompilation comp)
static void verify(CSharpCompilation comp, bool inferDelegate)
{
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
Expand All @@ -1722,19 +1704,19 @@ static void verify(CSharpCompilation comp)
// ... its first lambda ...
var firstLambda = tuple2.Arguments[1].Expression;
Assert.Null(model.GetTypeInfo(firstLambda).Type);
Assert.Equal("System.Delegate", model.GetTypeInfo(firstLambda).ConvertedType.ToTestDisplayString());
verifyType("System.Delegate", model.GetTypeInfo(firstLambda).ConvertedType, inferDelegate);

// ... its method group ...
var methodGroup = tuple2.Arguments[2].Expression;
Assert.Null(model.GetTypeInfo(methodGroup).Type);
Assert.Equal("System.Delegate", model.GetTypeInfo(methodGroup).ConvertedType.ToTestDisplayString());
verifyType("System.Delegate", model.GetTypeInfo(methodGroup).ConvertedType, inferDelegate);
Assert.Null(model.GetSymbolInfo(methodGroup).Symbol);
Assert.Equal(new[] { "void C.Main()" }, model.GetSymbolInfo(methodGroup).CandidateSymbols.Select(s => s.ToTestDisplayString()));

// ... its second lambda and the symbols it uses
var secondLambda = tuple2.Arguments[3].Expression;
Assert.Equal("System.Func<System.Int32, System.Int32>", model.GetTypeInfo(secondLambda).Type.ToTestDisplayString());
Assert.Equal("System.Delegate", model.GetTypeInfo(secondLambda).ConvertedType.ToTestDisplayString());
verifyType("System.Func<System.Int32, System.Int32>", model.GetTypeInfo(secondLambda).Type, inferDelegate);
verifyType("System.Delegate", model.GetTypeInfo(secondLambda).ConvertedType, inferDelegate);

var addition = tree.GetCompilationUnitRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().Last();
Assert.Equal("i + j", addition.ToString());
Expand All @@ -1745,6 +1727,18 @@ static void verify(CSharpCompilation comp)
var j = addition.Right;
Assert.Equal("System.Int32 j", model.GetSymbolInfo(j).Symbol.ToTestDisplayString());
}

static void verifyType(string expectedType, ITypeSymbol type, bool inferDelegate)
{
if (inferDelegate)
{
Assert.Equal(expectedType, type.ToTestDisplayString());
}
else
{
Assert.Null(type);
}
}
}

[Fact]
Expand Down Expand Up @@ -2017,9 +2011,9 @@ public void M()
// (6,13): error CS0815: Cannot assign (<null>, <null>) to an implicitly-typed variable
// var t = (null, null);
Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "t = (null, null)").WithArguments("(<null>, <null>)").WithLocation(6, 13),
// (7,22): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (7,13): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'lambda expression'
// if (null == (() => {}) ) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, "() => {}").WithArguments("inferred delegate type").WithLocation(7, 22),
Diagnostic(ErrorCode.ERR_BadBinaryOps, "null == (() => {})").WithArguments("==", "<null>", "lambda expression").WithLocation(7, 13),
// (8,13): error CS0019: Operator '==' cannot be applied to operands of type 'string' and 'int'
// if ("" == 1) {}
Diagnostic(ErrorCode.ERR_BadBinaryOps, @""""" == 1").WithArguments("==", "string", "int").WithLocation(8, 13)
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,9 @@ static void M(A a)
// (11,9): error CS1656: Cannot assign to 'E' because it is a 'method group'
// a.E += a.E;
Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "a.E").WithArguments("E", "method group").WithLocation(11, 9),
// (12,13): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (12,13): error CS0019: Operator '!=' cannot be applied to operands of type 'method group' and '<null>'
// if (a.E != null)
Diagnostic(ErrorCode.ERR_FeatureInPreview, "a.E").WithArguments("inferred delegate type").WithLocation(12, 13),
Diagnostic(ErrorCode.ERR_BadBinaryOps, "a.E != null").WithArguments("!=", "method group", "<null>").WithLocation(12, 13),
// (14,15): error CS1503: Argument 1: cannot convert from 'method group' to 'A'
// M(a.E);
Diagnostic(ErrorCode.ERR_BadArgType, "a.E").WithArguments("1", "method group", "A").WithLocation(14, 15),
Expand Down
Loading

0 comments on commit 82ce937

Please sign in to comment.