提交 975a50f6 编写于 作者: V vsadov

Making sure that "in" matches exactly in method group conversions.

It looks like method group conversions apply regular method overload resolution when resolving target method group.
However we know that in such scenario, unlike the regular argument passing, "byval" cannot match "in".

We should reject candidates with in/val mismatches when doing overload resolution for group conversion purposes. Otherwise we may encounter false ambiguity errors or even pick wrong candidates that crash at run time.

Fixes:#23319
上级 b4006aa2
......@@ -748,10 +748,11 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression receiverOpt, b
for (int i = 0; i < numParams; i++)
{
var delegateParameterType = delegateParameters[i].Type;
var methodParameterType = methodParameters[isExtensionMethod ? i + 1 : i].Type;
var delegateParameter = delegateParameters[i];
var methodParameter = methodParameters[isExtensionMethod ? i + 1 : i];
if (!Conversions.HasIdentityOrImplicitReferenceConversion(delegateParameterType, methodParameterType, ref useSiteDiagnostics))
if (delegateParameter.RefKind != methodParameter.RefKind ||
!Conversions.HasIdentityOrImplicitReferenceConversion(delegateParameter.Type, methodParameter.Type, ref useSiteDiagnostics))
{
// No overload for '{0}' matches delegate '{1}'
Error(diagnostics, ErrorCode.ERR_MethDelegateMismatch, errorLocation, method, delegateType);
......@@ -760,14 +761,14 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression receiverOpt, b
}
}
if (delegateMethod.ReturnsByRef != method.ReturnsByRef)
if (delegateMethod.RefKind != method.RefKind)
{
Error(diagnostics, ErrorCode.ERR_DelegateRefMismatch, errorLocation, method, delegateType);
diagnostics.Add(errorLocation, useSiteDiagnostics);
return false;
}
bool returnsMatch = delegateMethod.ReturnsByRef?
bool returnsMatch = delegateMethod.RefKind != RefKind.None ?
// - Return types identity-convertible
Conversions.HasIdentityConversion(method.ReturnType, delegateMethod.ReturnType):
// - Return types "match"
......
......@@ -356,7 +356,13 @@ private static bool OverloadResolutionResultIsValid<TMember>(ArrayBuilder<Member
return MemberAnalysisResult.UseSiteError();
}
var effectiveParameters = GetEffectiveParametersInNormalForm(constructor, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, arguments.RefKinds, allowRefOmittedArguments: false);
var effectiveParameters = GetEffectiveParametersInNormalForm(
constructor,
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion: false,
allowRefOmittedArguments: false);
return IsApplicable(
constructor,
......@@ -388,7 +394,13 @@ private static bool OverloadResolutionResultIsValid<TMember>(ArrayBuilder<Member
return MemberAnalysisResult.UseSiteError();
}
var effectiveParameters = GetEffectiveParametersInExpandedForm(constructor, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, arguments.RefKinds, allowRefOmittedArguments: false);
var effectiveParameters = GetEffectiveParametersInExpandedForm(
constructor,
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion: false,
allowRefOmittedArguments: false);
// A vararg ctor is never applicable in its expanded form because
// it is never a params method.
......@@ -2503,11 +2515,12 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re
int argumentCount,
ImmutableArray<int> argToParamMap,
ArrayBuilder<RefKind> argumentRefKinds,
bool isMethodGroupConversion,
bool allowRefOmittedArguments)
where TMember : Symbol
{
bool discarded;
return GetEffectiveParametersInNormalForm(member, argumentCount, argToParamMap, argumentRefKinds, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
return GetEffectiveParametersInNormalForm(member, argumentCount, argToParamMap, argumentRefKinds, isMethodGroupConversion, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
}
private EffectiveParameters GetEffectiveParametersInNormalForm<TMember>(
......@@ -2515,6 +2528,7 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re
int argumentCount,
ImmutableArray<int> argToParamMap,
ArrayBuilder<RefKind> argumentRefKinds,
bool isMethodGroupConversion,
bool allowRefOmittedArguments,
out bool hasAnyRefOmittedArgument) where TMember : Symbol
{
......@@ -2551,7 +2565,7 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re
types.Add(parameter.Type);
RefKind argRefKind = hasAnyRefArg ? argumentRefKinds[arg] : RefKind.None;
RefKind paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);
RefKind paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, isMethodGroupConversion, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);
if (refs == null)
{
......@@ -2571,12 +2585,18 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re
return new EffectiveParameters(types.ToImmutableAndFree(), refKinds);
}
private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind argRefKind, bool allowRefOmittedArguments, ref bool hasAnyRefOmittedArgument)
private RefKind GetEffectiveParameterRefKind(
ParameterSymbol parameter,
RefKind argRefKind,
bool isMethodGroupConversion,
bool allowRefOmittedArguments,
ref bool hasAnyRefOmittedArgument)
{
var paramRefKind = parameter.RefKind;
// 'None' argument is allowed to match 'In' parameter and should behave like 'None' for the purpose of overload resolution
if (argRefKind == RefKind.None && paramRefKind == RefKind.In)
// unless this is a method group conversion where 'In' must match 'In'
if (!isMethodGroupConversion && argRefKind == RefKind.None && paramRefKind == RefKind.In)
{
return RefKind.None;
}
......@@ -2598,10 +2618,11 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
int argumentCount,
ImmutableArray<int> argToParamMap,
ArrayBuilder<RefKind> argumentRefKinds,
bool isMethodGroupConversion,
bool allowRefOmittedArguments) where TMember : Symbol
{
bool discarded;
return GetEffectiveParametersInExpandedForm(member, argumentCount, argToParamMap, argumentRefKinds, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
return GetEffectiveParametersInExpandedForm(member, argumentCount, argToParamMap, argumentRefKinds, isMethodGroupConversion, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
}
private EffectiveParameters GetEffectiveParametersInExpandedForm<TMember>(
......@@ -2609,6 +2630,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
int argumentCount,
ImmutableArray<int> argToParamMap,
ArrayBuilder<RefKind> argumentRefKinds,
bool isMethodGroupConversion,
bool allowRefOmittedArguments,
out bool hasAnyRefOmittedArgument) where TMember : Symbol
{
......@@ -2629,7 +2651,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
types.Add(parm == parameters.Length - 1 ? elementType : parameter.Type);
var argRefKind = hasAnyRefArg ? argumentRefKinds[arg] : RefKind.None;
var paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);
var paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, isMethodGroupConversion, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);
refs.Add(paramRefKind);
if (paramRefKind != RefKind.None)
......@@ -2689,6 +2711,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion,
allowRefOmittedArguments,
out hasAnyRefOmittedArgument);
......@@ -2700,6 +2723,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion,
allowRefOmittedArguments);
// The member passed to the following call is returned in the result (possibly a constructed version of it).
......@@ -2756,6 +2780,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion: false,
allowRefOmittedArguments,
out hasAnyRefOmittedArgument);
......@@ -2767,6 +2792,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
arguments.Arguments.Count,
argumentAnalysis.ArgsToParamsOpt,
arguments.RefKinds,
isMethodGroupConversion: false,
allowRefOmittedArguments);
// The member passed to the following call is returned in the result (possibly a constructed version of it).
......@@ -3115,13 +3141,11 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
// exists from the argument to the type of the corresponding parameter, or
// - for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter.
// RefKind has to match unless
// the ref kind is None and either:
// 1) parameter is an 'In' or
// 2) argument expression is of the type dynamic. This is a bug in Dev11 which we also implement.
// effective RefKind has to match unless argument expression is of the type dynamic.
// This is a bug in Dev11 which we also implement.
// The spec is correct, this is not an intended behavior. We don't fix the bug to avoid a breaking change.
if (!(argRefKind == parRefKind ||
(argRefKind == RefKind.None && (parRefKind == RefKind.In || argument.HasDynamicType()))))
(argRefKind == RefKind.None && argument.HasDynamicType())))
{
return Conversion.NoConversion;
}
......
......@@ -10036,5 +10036,158 @@ void M(C c, in int y)
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(14, 10)
);
}
[Fact]
public void MethodGroupConversionVal2In()
{
var code = @"
using System;
class Program
{
static void F(in DateTime x)
{
Console.WriteLine(x);
}
static void Main()
{
Action<DateTime> a = F;
a(DateTime.MaxValue);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics(
// (13,30): error CS0123: No overload for 'F' matches delegate 'Action<DateTime>'
// Action<DateTime> a = F;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "F").WithArguments("F", "System.Action<System.DateTime>").WithLocation(13, 30)
);
}
[Fact]
public void MethodGroupConversionVal2Overloaded()
{
var code = @"
using System;
class Program
{
static void F(in DateTime x)
{
Console.WriteLine('1');
}
static void F(DateTime x)
{
Console.WriteLine('2');
}
static void Main()
{
Action<DateTime> a = F;
a(DateTime.MaxValue);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics(
);
}
[Fact]
public void MethodGroupConversionIn2Overloaded()
{
var code = @"
using System;
class Program
{
delegate void D(in DateTime d);
static void F(in DateTime x)
{
Console.WriteLine('1');
}
static void F(DateTime x)
{
Console.WriteLine('2');
}
static void Main()
{
D a = F;
a(DateTime.MaxValue);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics();
}
[Fact]
public void MethodGroupConversionRoReadonlyReturn()
{
var code = @"
using System;
class Program
{
delegate int D(in DateTime d);
static ref readonly int F(in DateTime x)
{
Console.WriteLine('1');
return ref (new int[1])[0];
}
static void Main()
{
D a = F;
a(DateTime.MaxValue);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics
(
// (16,15): error CS8189: Ref mismatch between 'Program.F(in DateTime)' and delegate 'Program.D'
// D a = F;
Diagnostic(ErrorCode.ERR_DelegateRefMismatch, "F").WithArguments("Program.F(in System.DateTime)", "Program.D").WithLocation(16, 15)
);
}
[Fact]
public void MethodGroupConversionRoReadonlyReturnType()
{
var code = @"
using System;
class Program
{
delegate ref readonly object D(in DateTime d);
static ref readonly string F(in DateTime x)
{
Console.WriteLine('1');
return ref (new string[1])[0];
}
static void Main()
{
D a = F;
a(DateTime.MaxValue);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics
(
// (16,15): error CS0407: 'string Program.F(in DateTime)' has the wrong return type
// D a = F;
Diagnostic(ErrorCode.ERR_BadRetType, "F").WithArguments("Program.F(in System.DateTime)", "string").WithLocation(16, 15)
);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册