From c23414e1365f21c6bd34ab5b007546066f25b5d4 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 8 May 2024 19:42:19 -0700 Subject: [PATCH] Fix overload resolution regression around params parameters (#73373) Fixes #73346. --- .../Portable/Binder/Binder_Expressions.cs | 2 +- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../MemberAnalysisResult.cs | 13 +- .../OverloadResolution/OverloadResolution.cs | 121 ++++++++++-------- .../Emit2/Semantics/ParamsCollectionTests.cs | 112 ++++++++++++++++ 5 files changed, 191 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 3fb4a0c6d552d..cb4fa6f2bf9e3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -10627,7 +10627,7 @@ bool satisfiesConstraintChecks(MethodSymbol method) parameters.SelectAsArray(p => p.ExplicitDefaultConstantValue) : default; - var hasParams = OverloadResolution.IsValidParams(this, methodSymbol); + var hasParams = OverloadResolution.IsValidParams(this, methodSymbol, out _); Debug.Assert(ContainingMemberOrLambda is { }); Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index e7c92744a4381..6ff5e6c47c6ba 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -882,7 +882,7 @@ private void ReportDynamicInvocationWarnings(SyntaxNode syntax, BoundMethodGroup private bool IsAmbiguousDynamicParamsArgument(ArrayBuilder arguments, MemberResolutionResult candidate, out SyntaxNode argumentSyntax) where TMethodOrPropertySymbol : Symbol { - if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember) && + if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember, out _) && candidate.Result.Kind == MemberResolutionKind.ApplicableInNormalForm) { var parameters = candidate.Member.GetParameters(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index f0a6c46065c3a..d60aa1ae31254 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -105,6 +105,7 @@ private init public readonly int BadParameter; public readonly MemberResolutionKind Kind; + public readonly TypeWithAnnotations DefinitionParamsElementTypeOpt; public readonly TypeWithAnnotations ParamsElementTypeOpt; /// @@ -121,11 +122,14 @@ private MemberAnalysisResult( int missingParameter = -1, bool hasAnyRefOmittedArgument = false, ImmutableArray constraintFailureDiagnosticsOpt = default, + TypeWithAnnotations definitionParamsElementTypeOpt = default, TypeWithAnnotations paramsElementTypeOpt = default) { + Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || definitionParamsElementTypeOpt.HasType); Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || paramsElementTypeOpt.HasType); this.Kind = kind; + this.DefinitionParamsElementTypeOpt = definitionParamsElementTypeOpt; this.ParamsElementTypeOpt = paramsElementTypeOpt; this.BadArgumentsOpt = badArgumentsOpt; this.ArgsToParamsOpt = argsToParamsOpt; @@ -314,7 +318,7 @@ public static MemberAnalysisResult UnsupportedMetadata() return new MemberAnalysisResult(MemberResolutionKind.UnsupportedMetadata); } - public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations paramsElementTypeOpt) + public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations definitionParamsElementTypeOpt, TypeWithAnnotations paramsElementTypeOpt) { Debug.Assert(conversions.Length != 0); Debug.Assert(badArguments.TrueBits().Any()); @@ -323,6 +327,7 @@ public static MemberAnalysisResult BadArgumentConversions(ImmutableArray ar badArguments, argsToParamsOpt, conversions, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } @@ -373,9 +378,11 @@ public static MemberAnalysisResult NormalForm(ImmutableArray argsToParamsOp return new MemberAnalysisResult(MemberResolutionKind.ApplicableInNormalForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument); } - public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations paramsElementType) + public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations definitionParamsElementType, TypeWithAnnotations paramsElementType) { - return new MemberAnalysisResult(MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, paramsElementTypeOpt: paramsElementType); + return new MemberAnalysisResult( + MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, definitionParamsElementTypeOpt: definitionParamsElementType, paramsElementTypeOpt: paramsElementType); } public static MemberAnalysisResult Worse() diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 385db1686b016..5acde0dfa8f0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -817,9 +817,9 @@ private void AddConstructorToCandidateSet(MethodSymbol constructor, ArrayBuilder var result = normalResult; if (!normalResult.IsValid) { - if (IsValidParams(_binder, constructor)) + if (IsValidParams(_binder, constructor, out TypeWithAnnotations definitionElementType)) { - var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, completeResults, ref useSiteInfo); + var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, definitionElementType, completeResults, ref useSiteInfo); if (expandedResult.IsValid || completeResults) { result = expandedResult; @@ -864,6 +864,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( return IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -878,6 +879,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( private MemberAnalysisResult IsConstructorApplicableInExpandedForm( MethodSymbol constructor, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool completeResults, ref CompoundUseSiteInfo useSiteInfo) { @@ -906,6 +908,7 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm( var result = IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: definitionParamsElementType, isExpanded: true, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -1038,7 +1041,7 @@ private void AddMemberToCandidateSet( // Second, we need to determine if the method is applicable in its normal form or its expanded form. - var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember)) + var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember, out _)) ? default(MemberResolutionResult) : IsMemberApplicableInNormalForm( member, @@ -1057,13 +1060,14 @@ private void AddMemberToCandidateSet( // tricks you can pull to make overriding methods [indexers] inconsistent with overridden // methods [indexers] (or implementing methods [indexers] inconsistent with interfaces). - if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember)) + if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember, out TypeWithAnnotations definitionElementType)) { var expandedResult = IsMemberApplicableInExpandedForm( member, leastOverriddenMember, typeArguments, arguments, + definitionElementType, allowRefOmittedArguments: (options & Options.AllowRefOmittedArguments) != 0, completeResults: completeResults, dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, @@ -1156,17 +1160,19 @@ static bool haveBadArgumentForLastParameter(MemberResolutionResult resu // We need to know if this is a valid formal parameter list with a parameter array // as the final formal parameter. We might be in an error recovery scenario // where the params array is not an array type. - public static bool IsValidParams(Binder binder, Symbol member) + public static bool IsValidParams(Binder binder, Symbol member, out TypeWithAnnotations definitionElementType) { // A varargs method is never a valid params method. if (member.GetIsVararg()) { + definitionElementType = default; return false; } int paramCount = member.GetParameterCount(); if (paramCount == 0) { + definitionElementType = default; return false; } @@ -1175,9 +1181,10 @@ public static bool IsValidParams(Binder binder, Symbol member) (final.IsParamsCollection && !final.Type.IsSZArray() && (binder.Compilation.LanguageVersion > LanguageVersion.CSharp12 || member.ContainingModule == binder.Compilation.SourceModule))) { - return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out _); + return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out definitionElementType); } + definitionElementType = default; return false; } @@ -1837,25 +1844,6 @@ private void RemoveWorseMembers(ArrayBuilder - /// Returns the parameter type (considering params). - /// - private static TypeSymbol GetParameterType(ParameterSymbol parameter, MemberAnalysisResult result, int parameterCount) - { - var type = parameter.Type; - if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && - parameter.Ordinal == parameterCount - 1) - { - Debug.Assert(result.ParamsElementTypeOpt.HasType); - Debug.Assert(result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); - return result.ParamsElementTypeOpt.Type; - } - else - { - return type; - } - } - /// /// Returns the parameter corresponding to the given argument index. /// @@ -1955,11 +1943,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out RefKind parameter1RefKind); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out RefKind parameter2RefKind); bool okToDowngradeToNeither; BetterResult r; @@ -1967,10 +1953,10 @@ private BetterResult BetterFunctionMember( r = BetterConversionFromExpression(arguments[i], type1, m1.Result.ConversionForArg(i), - parameter1.RefKind, + parameter1RefKind, type2, m2.Result.ConversionForArg(i), - parameter2.RefKind, + parameter2RefKind, considerRefKinds, ref useSiteInfo, out okToDowngradeToNeither); @@ -2099,11 +2085,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out _); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out _); var type1Normalized = type1; var type2Normalized = type2; @@ -2248,8 +2232,8 @@ private BetterResult BetterFunctionMember( using (var uninst1 = TemporaryArray.Empty) using (var uninst2 = TemporaryArray.Empty) { - var m1Original = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); - var m2Original = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m1DefinitionParameters = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m2DefinitionParameters = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); for (i = 0; i < arguments.Count; ++i) { // If these are both applicable varargs methods and we're looking at the __arglist argument @@ -2261,11 +2245,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1Original); - uninst1.Add(GetParameterType(parameter1, m1.Result, m1Original.Length)); + uninst1.Add(getParameterTypeAndRefKind(i, m1.Result, m1DefinitionParameters, m1.Result.DefinitionParamsElementTypeOpt, out _)); - var parameter2 = GetParameter(i, m2.Result, m2Original); - uninst2.Add(GetParameterType(parameter2, m2.Result, m2Original.Length)); + uninst2.Add(getParameterTypeAndRefKind(i, m2.Result, m2DefinitionParameters, m2.Result.DefinitionParamsElementTypeOpt, out _)); } result = MoreSpecificType(ref uninst1.AsRef(), ref uninst2.AsRef(), ref useSiteInfo); @@ -2353,6 +2335,26 @@ private BetterResult BetterFunctionMember( } return BetterResult.Neither; + + // Returns the parameter type (considering params). + static TypeSymbol getParameterTypeAndRefKind(int i, MemberAnalysisResult result, ImmutableArray parameters, TypeWithAnnotations paramsElementTypeOpt, out RefKind parameter1RefKind) + { + var parameter = GetParameter(i, result, parameters); + parameter1RefKind = parameter.RefKind; + + var type = parameter.Type; + if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && + parameter.Ordinal == parameters.Length - 1) + { + Debug.Assert(paramsElementTypeOpt.HasType); + Debug.Assert(paramsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); + return paramsElementTypeOpt.Type; + } + else + { + return type; + } + } } /// @@ -3691,7 +3693,7 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( TMember leastOverriddenMemberConstructedFrom = GetConstructedFrom(leastOverriddenMember); bool hasAnyRefOmittedArgument; - EffectiveParameters originalEffectiveParameters = GetEffectiveParametersInNormalForm( + EffectiveParameters constructedFromEffectiveParameters = GetEffectiveParametersInNormalForm( leastOverriddenMemberConstructedFrom, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, @@ -3706,7 +3708,8 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( // The applicability is checked based on effective parameters passed in. var applicableResult = IsApplicable( member, leastOverriddenMemberConstructedFrom, - typeArguments, arguments, originalEffectiveParameters, + typeArguments, arguments, constructedFromEffectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, @@ -3730,6 +3733,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm typeArguments, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool allowRefOmittedArguments, bool completeResults, bool dynamicConvertsToAnything, @@ -3754,7 +3758,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm IsMemberApplicableInExpandedForm IsApplicable( TMember leastOverriddenMember, // method or property ArrayBuilder typeArgumentsBuilder, AnalyzedArguments arguments, - EffectiveParameters originalEffectiveParameters, + EffectiveParameters constructedFromEffectiveParameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, ImmutableArray argsToParamsMap, bool hasAnyRefOmittedArgument, @@ -3836,7 +3842,7 @@ private MemberResolutionResult IsApplicable( typeArguments = InferMethodTypeArguments(method, leastOverriddenMethod.ConstructedFrom.TypeParameters, arguments, - originalEffectiveParameters, + constructedFromEffectiveParameters, out hasTypeArgumentsInferredFromFunctionType, out inferenceError, ref useSiteInfo); @@ -3892,19 +3898,20 @@ private MemberResolutionResult IsApplicable( var map = new TypeMap(leastOverriddenMethod.TypeParameters, typeArguments, allowAlpha: true); constructedEffectiveParameters = new EffectiveParameters( - map.SubstituteTypes(originalEffectiveParameters.ParameterTypes), - originalEffectiveParameters.ParameterRefKinds, - originalEffectiveParameters.FirstParamsElementIndex); + map.SubstituteTypes(constructedFromEffectiveParameters.ParameterTypes), + constructedFromEffectiveParameters.ParameterRefKinds, + constructedFromEffectiveParameters.FirstParamsElementIndex); } else { - constructedEffectiveParameters = originalEffectiveParameters; + constructedEffectiveParameters = constructedFromEffectiveParameters; ignoreOpenTypes = false; } var applicableResult = IsApplicable( member, constructedEffectiveParameters, + definitionParamsElementTypeOpt, isExpanded, arguments, argsToParamsMap, @@ -3975,6 +3982,7 @@ private ImmutableArray InferMethodTypeArguments( private MemberAnalysisResult IsApplicable( Symbol candidate, // method or property EffectiveParameters parameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, AnalyzedArguments arguments, ImmutableArray argsToParameters, @@ -4113,7 +4121,8 @@ private MemberAnalysisResult IsApplicable( // of overloads for the semantic model. Debug.Assert(badArguments.IsNull); Debug.Assert(conversions == null); - return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), paramsElementTypeOpt); + return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } if (!conversion.Exists) @@ -4148,12 +4157,16 @@ private MemberAnalysisResult IsApplicable( var conversionsArray = conversions != null ? conversions.ToImmutableAndFree() : default(ImmutableArray); if (!badArguments.IsNull) { - result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, paramsElementTypeOpt); + result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, + paramsElementTypeOpt: paramsElementTypeOpt); } else if (isExpanded) { Debug.Assert(paramsElementTypeOpt.HasType); - result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, paramsElementTypeOpt); + result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, + definitionParamsElementType: definitionParamsElementTypeOpt, + paramsElementType: paramsElementTypeOpt); } else { diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 81467ecb958c5..b512928223c16 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -15650,5 +15650,117 @@ static void Main() Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14) ); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_01() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params Func[] projections) => Console.Write(1); + public void Method(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_02() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public C(params Func[] projections) => Console.Write(1); + public C(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C>(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_03() + { + string source = """ +using System; +using System.Collections.Generic; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params IEnumerable> projections) => Console.Write(1); + public void Method(params IEnumerable>> projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } } }