提交 8ee822e1 编写于 作者: A AlekseyTs 提交者: GitHub

Merge pull request #16079 from AlekseyTs/Bug296550

Fix a crash caused by an improper reuse of lambdas from return inference cache.
......@@ -110,6 +110,9 @@ public TypeSymbol InferredReturnType(ref HashSet<DiagnosticInfo> useSiteDiagnost
return _inferredReturnType;
}
/// <summary>
/// Behavior of this function should be kept aligned with <see cref="UnboundLambdaState.ReturnInferenceCacheKey"/>.
/// </summary>
private static TypeSymbol InferReturnType(
BoundBlock block,
Binder binder,
......@@ -148,12 +151,12 @@ public TypeSymbol InferredReturnType(ref HashSet<DiagnosticInfo> useSiteDiagnost
// Otherwise the return type is Task or Task<T>.
NamedTypeSymbol taskType = null;
var delegateReturnType = delegateType?.GetDelegateType()?.DelegateInvokeMethod?.ReturnType as NamedTypeSymbol;
if ((object)delegateReturnType != null)
if ((object)delegateReturnType != null && delegateReturnType.SpecialType != SpecialType.System_Void)
{
object builderType;
if (delegateReturnType.IsCustomTaskType(out builderType))
{
taskType = delegateReturnType;
taskType = delegateReturnType.ConstructedFrom;
}
}
......@@ -176,7 +179,7 @@ public TypeSymbol InferredReturnType(ref HashSet<DiagnosticInfo> useSiteDiagnost
// Some non-void best type T was found; use delegate InvokeMethod
// or infer type Task<T> if delegate type not available.
var taskTypeT = (object)taskType != null && taskType.Arity == 1 ?
delegateReturnType.ConstructedFrom :
taskType :
binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T);
return taskTypeT.Construct(bestResultType);
}
......@@ -294,10 +297,9 @@ internal abstract class UnboundLambdaState
{
private UnboundLambda _unboundLambda; // we would prefer this readonly, but we have an initialization cycle.
protected readonly Binder binder;
private readonly ConcurrentDictionary<object, BoundLambda> _bindingCache = new ConcurrentDictionary<object, BoundLambda>();
private readonly ConcurrentDictionary<NamedTypeSymbol, BoundLambda> _bindingCache = new ConcurrentDictionary<NamedTypeSymbol, BoundLambda>();
private readonly ConcurrentDictionary<MethodSymbol, BoundLambda> _returnInferenceCache =
new ConcurrentDictionary<MethodSymbol, BoundLambda>(MemberSignatureComparer.LambdaReturnInferenceCacheComparer);
private readonly ConcurrentDictionary<ReturnInferenceCacheKey, BoundLambda> _returnInferenceCache = new ConcurrentDictionary<ReturnInferenceCacheKey, BoundLambda>();
private BoundLambda _errorBinding;
......@@ -424,35 +426,36 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
// when binding for real (not for return inference), there is still
// a good chance that we could reuse a body of a lambda previously bound for
// return type inference.
MethodSymbol cacheKey = GetCacheKey(delegateType);
var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync);
BoundLambda returnInferenceLambda;
if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType && returnInferenceLambda.Symbol.ReturnType == returnType)
if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType)
{
lambdaSymbol = returnInferenceLambda.Symbol;
Debug.Assert(lambdaSymbol.RefKind == refKind);
if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaSymbol.ReturnType &&
lambdaSymbol.ReturnType == returnType && lambdaSymbol.RefKind == refKind)
{
lambdaBodyBinder = returnInferenceLambda.Binder;
block = returnInferenceLambda.Body;
diagnostics.AddRange(returnInferenceLambda.Diagnostics);
goto haveLambdaBodyAndBinders;
}
var parameters = DelegateParameters(invokeMethod);
}
lambdaSymbol = new LambdaSymbol(
binder.Compilation,
binder.ContainingMemberOrLambda,
_unboundLambda,
parameters,
cacheKey.ParameterTypes,
cacheKey.ParameterRefKinds,
refKind,
returnType);
lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics);
((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics);
ValidateUnsafeParameters(diagnostics, parameters);
ValidateUnsafeParameters(diagnostics, cacheKey.ParameterTypes);
haveLambdaBodyAndBinders:
......@@ -488,28 +491,13 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.Locations[0]);
}
// This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481
if ((object)returnType != null && returnType.SpecialType != SpecialType.System_Void &&
!block.HasErrors && !diagnostics.HasAnyResolvedErrors() && block.Statements.Length > 0)
{
BoundStatement first = block.Statements[0];
if (first.Kind == BoundKind.ReturnStatement)
{
var returnStmt = (BoundReturnStatement)first;
if (returnStmt.ExpressionOpt != null && (object)returnStmt.ExpressionOpt.Type == null)
{
throw ExceptionUtilities.Unreachable;
}
}
}
var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false)
{ WasCompilerGenerated = _unboundLambda.WasCompilerGenerated };
return result;
}
private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray<ParameterSymbol> parameters)
private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray<TypeSymbol> targetParameterTypes)
{
// It is legal to use a delegate type that has unsafe parameter types inside
// a safe context if the anonymous method has no parameter list!
......@@ -521,13 +509,12 @@ private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray<
if (this.HasSignature)
{
// NOTE: we can get here with parameters.Length > ParameterCount
// NOTE: we can get here with targetParameterTypes.Length > ParameterCount
// in a case where we are binding for error reporting purposes
var numParametersToCheck = Math.Min(parameters.Length, ParameterCount);
var numParametersToCheck = Math.Min(targetParameterTypes.Length, ParameterCount);
for (int i = 0; i < numParametersToCheck; i++)
{
ParameterSymbol parameter = parameters[i];
if (parameter.Type.IsUnsafe())
if (targetParameterTypes[i].IsUnsafe())
{
this.binder.ReportUnsafeIfNotAllowed(this.ParameterLocation(i), diagnostics);
}
......@@ -535,12 +522,10 @@ private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray<
}
}
private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType)
private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType, ImmutableArray<TypeSymbol> parameterTypes, ImmutableArray<RefKind> parameterRefKinds)
{
var diagnostics = DiagnosticBag.GetInstance();
var invokeMethod = DelegateInvokeMethod(delegateType);
var parameters = DelegateParameters(invokeMethod);
var lambdaSymbol = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, parameters, refKind: Microsoft.CodeAnalysis.RefKind.None, returnType: null);
var lambdaSymbol = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, parameterTypes, parameterRefKinds, refKind: Microsoft.CodeAnalysis.RefKind.None, returnType: null);
Binder lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics);
......@@ -548,7 +533,7 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType)
{ WasCompilerGenerated = _unboundLambda.WasCompilerGenerated };
HashSet<DiagnosticInfo> useSiteDiagnostics = null; // TODO: figure out if this should be somehow merged into BoundLambda.Diagnostics.
TypeSymbol returnType = result.InferredReturnType(ref useSiteDiagnostics) ?? new ExtendedErrorTypeSymbol(binder.Compilation, string.Empty, 0, null);
TypeSymbol returnType = result.InferredReturnType(ref useSiteDiagnostics) ?? LambdaSymbol.InferenceFailureReturnType;
lambdaSymbol.SetInferredReturnType(result.RefKind, returnType);
return result;
......@@ -556,35 +541,104 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType)
public BoundLambda BindForReturnTypeInference(NamedTypeSymbol delegateType)
{
MethodSymbol cacheKey = GetCacheKey(delegateType);
var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync);
BoundLambda result;
if (!_returnInferenceCache.TryGetValue(cacheKey, out result))
{
result = ReallyInferReturnType(delegateType);
result = ReallyInferReturnType(delegateType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds);
_returnInferenceCache.TryAdd(cacheKey, result);
// In case the return value of the BoundLambda is distinct from
// delegateType, add the BoundLambda to the cache by the
// distinct signature as well.
_returnInferenceCache.TryAdd(result.Symbol, result);
}
return result;
}
private MethodSymbol GetCacheKey(NamedTypeSymbol delegateType)
/// <summary>
/// Behavior of this key should be kept aligned with <see cref="BoundLambda.InferReturnType"/>.
/// </summary>
internal class ReturnInferenceCacheKey
{
var invoke = DelegateInvokeMethod(delegateType);
if ((object)invoke != null)
public readonly ImmutableArray<TypeSymbol> ParameterTypes;
public readonly ImmutableArray<RefKind> ParameterRefKinds;
public readonly NamedTypeSymbol TaskLikeReturnTypeOpt;
public static readonly ReturnInferenceCacheKey Empty = new ReturnInferenceCacheKey(ImmutableArray<TypeSymbol>.Empty, ImmutableArray<RefKind>.Empty, null);
private ReturnInferenceCacheKey(ImmutableArray<TypeSymbol> parameterTypes, ImmutableArray<RefKind> parameterRefKinds, NamedTypeSymbol taskLikeReturnTypeOpt)
{
return invoke;
Debug.Assert(parameterTypes.Length == parameterRefKinds.Length);
Debug.Assert((object)taskLikeReturnTypeOpt == null || ((object)taskLikeReturnTypeOpt == taskLikeReturnTypeOpt.ConstructedFrom && taskLikeReturnTypeOpt.IsCustomTaskType(out var builderArgument)));
this.ParameterTypes = parameterTypes;
this.ParameterRefKinds = parameterRefKinds;
this.TaskLikeReturnTypeOpt = taskLikeReturnTypeOpt;
}
public override bool Equals(object obj)
{
if ((object)this == obj)
{
return true;
}
var other = obj as ReturnInferenceCacheKey;
return (object)other != null &&
other.ParameterTypes.SequenceEqual(this.ParameterTypes) &&
other.ParameterRefKinds.SequenceEqual(this.ParameterRefKinds) &&
other.TaskLikeReturnTypeOpt == this.TaskLikeReturnTypeOpt;
}
public override int GetHashCode()
{
return Hash.Combine(TaskLikeReturnTypeOpt?.GetHashCode() ?? 0, Hash.CombineValues(this.ParameterTypes));
}
public static ReturnInferenceCacheKey Create(NamedTypeSymbol delegateType, bool isAsync)
{
// delegateType or DelegateInvokeMethod can be null in cases of malformed delegates
// in such case we would want something trivial with no parameters, like a fake static ctor.
// Since the containingType of the .cctor must be non-null, System.Object is used.
return new SynthesizedStaticConstructor(binder.Compilation.GetSpecialType(SpecialType.System_Object));
// in such case we would want something trivial with no parameters
var parameterTypes = ImmutableArray<TypeSymbol>.Empty;
var parameterRefKinds = ImmutableArray<RefKind>.Empty;
NamedTypeSymbol taskLikeReturnTypeOpt = null;
MethodSymbol invoke = DelegateInvokeMethod(delegateType);
if ((object)invoke != null)
{
int parameterCount = invoke.ParameterCount;
if (parameterCount > 0)
{
var typesBuilder = ArrayBuilder<TypeSymbol>.GetInstance(parameterCount);
var refKindsBuilder = ArrayBuilder<RefKind>.GetInstance(parameterCount);
foreach (var p in invoke.Parameters)
{
refKindsBuilder.Add(p.RefKind);
typesBuilder.Add(p.Type);
}
parameterTypes = typesBuilder.ToImmutableAndFree();
parameterRefKinds = refKindsBuilder.ToImmutableAndFree();
}
if (isAsync)
{
var delegateReturnType = invoke.ReturnType as NamedTypeSymbol;
if ((object)delegateReturnType != null && delegateReturnType.SpecialType != SpecialType.System_Void)
{
if (delegateReturnType.IsCustomTaskType(out var builderType))
{
taskLikeReturnTypeOpt = delegateReturnType.ConstructedFrom;
}
}
}
}
if (parameterTypes.IsEmpty && parameterRefKinds.IsEmpty && (object)taskLikeReturnTypeOpt == null)
{
return Empty;
}
return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, taskLikeReturnTypeOpt);
}
}
public TypeSymbol InferReturnType(NamedTypeSymbol delegateType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
......@@ -639,7 +693,7 @@ private BoundLambda ReallyBindForErrorRecovery()
return
GuessBestBoundLambda(_bindingCache.Values)
?? GuessBestBoundLambda(_returnInferenceCache.Values)
?? ReallyInferReturnType(null);
?? ReallyInferReturnType(null, ImmutableArray<TypeSymbol>.Empty, ImmutableArray<RefKind>.Empty);
}
private static BoundLambda GuessBestBoundLambda(ICollection<BoundLambda> candidates)
......
......@@ -274,9 +274,6 @@ private static Binder GetEnclosingBinder(SyntaxNode node, int position, Binder r
{
binder = new ExecutableCodeBinder(unexpectedAnonymousFunction,
new LambdaSymbol(binder.ContainingMemberOrLambda,
ImmutableArray<ParameterSymbol>.Empty,
RefKind.None,
ErrorTypeSymbol.UnknownResultType,
unexpectedAnonymousFunction.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda,
unexpectedAnonymousFunction,
isSynthesized: false),
......
......@@ -963,12 +963,6 @@ public BoundExpression Null(TypeSymbol type)
public BoundTypeExpression Type(TypeSymbol type)
{
// This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481
if ((object)type == null)
{
throw ExceptionUtilities.Unreachable;
}
return new BoundTypeExpression(Syntax, null, type) { WasCompilerGenerated = true };
}
......
......@@ -262,21 +262,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerRefOutDifference: true,
considerCustomModifiers: false);
/// <summary>
/// This instance is used as a key in the lambda return type inference.
/// We basically only interested in parameters since inference will set the return type to null.
/// </summary>
public static readonly MemberSignatureComparer LambdaReturnInferenceCacheComparer = new MemberSignatureComparer(
considerName: false, // valid invoke is always called "Invoke"
considerExplicitlyImplementedInterfaces: false,
considerReturnType: true, // to differentiate Task types
considerTypeConstraints: false, // valid invoke is never generic
considerCallingConvention: false, // valid invoke is never static
considerRefOutDifference: true,
considerCustomModifiers: true,
ignoreDynamic: false,
ignoreTupleNames: false);
// Compare the "unqualified" part of the member name (no explicit part)
private readonly bool _considerName;
......
......@@ -18,11 +18,17 @@ internal sealed class LambdaSymbol : MethodSymbol
private readonly bool _isSynthesized;
private readonly bool _isAsync;
/// <summary>
/// This symbol is used as the return type of a LambdaSymbol when we failed to infer its return type.
/// </summary>
internal static readonly TypeSymbol InferenceFailureReturnType = new UnsupportedMetadataTypeSymbol();
public LambdaSymbol(
CSharpCompilation compilation,
Symbol containingSymbol,
UnboundLambda unboundLambda,
ImmutableArray<ParameterSymbol> delegateParameters,
ImmutableArray<TypeSymbol> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds,
RefKind refKind,
TypeSymbol returnType)
{
......@@ -34,14 +40,11 @@ internal sealed class LambdaSymbol : MethodSymbol
_isSynthesized = unboundLambda.WasCompilerGenerated;
_isAsync = unboundLambda.IsAsync;
// No point in making this lazy. We are always going to need these soon after creation of the symbol.
_parameters = MakeParameters(compilation, unboundLambda, delegateParameters);
_parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds);
}
public LambdaSymbol(
Symbol containingSymbol,
ImmutableArray<ParameterSymbol> parameters,
RefKind refKind,
TypeSymbol returnType,
MessageID messageID,
SyntaxNode syntax,
bool isSynthesized)
......@@ -49,10 +52,10 @@ internal sealed class LambdaSymbol : MethodSymbol
_containingSymbol = containingSymbol;
_messageID = messageID;
_syntax = syntax;
_refKind = refKind;
_returnType = returnType;
_refKind = RefKind.None;
_returnType = ErrorTypeSymbol.UnknownResultType;
_isSynthesized = isSynthesized;
_parameters = parameters.SelectAsArray(CopyParameter, this);
_parameters = ImmutableArray<ParameterSymbol>.Empty;
}
public MessageID MessageID { get { return _messageID; } }
......@@ -287,17 +290,27 @@ public override bool HidesBaseMethodsByName
private ImmutableArray<ParameterSymbol> MakeParameters(
CSharpCompilation compilation,
UnboundLambda unboundLambda,
ImmutableArray<ParameterSymbol> delegateParameters)
ImmutableArray<TypeSymbol> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds)
{
Debug.Assert(parameterTypes.Length == parameterRefKinds.Length);
if (!unboundLambda.HasSignature || unboundLambda.ParameterCount == 0)
{
// The parameters may be omitted in source, but they are still present on the symbol.
return delegateParameters.SelectAsArray(CopyParameter, this);
return parameterTypes.SelectAsArray((type, ordinal, arg) =>
SynthesizedParameterSymbol.Create(
arg.owner,
type,
ordinal,
arg.refKinds[ordinal],
GeneratedNames.LambdaCopyParameterName(ordinal)), // Make sure nothing binds to this.
(owner: this, refKinds: parameterRefKinds));
}
var builder = ArrayBuilder<ParameterSymbol>.GetInstance();
var hasExplicitlyTypedParameterList = unboundLambda.HasExplicitlyTypedParameterList;
var numDelegateParameters = delegateParameters.IsDefault ? 0 : delegateParameters.Length;
var numDelegateParameters = parameterTypes.Length;
for (int p = 0; p < unboundLambda.ParameterCount; ++p)
{
......@@ -316,9 +329,8 @@ public override bool HidesBaseMethodsByName
}
else if (p < numDelegateParameters)
{
ParameterSymbol delegateParameter = delegateParameters[p];
type = delegateParameter.Type;
refKind = delegateParameter.RefKind;
type = parameterTypes[p];
refKind = parameterRefKinds[p];
}
else
{
......@@ -339,16 +351,6 @@ public override bool HidesBaseMethodsByName
return result;
}
private static ParameterSymbol CopyParameter(ParameterSymbol parameter, MethodSymbol owner)
{
return SynthesizedParameterSymbol.Create(
owner,
parameter.Type,
parameter.Ordinal,
parameter.RefKind,
GeneratedNames.LambdaCopyParameterName(parameter)); // Make sure nothing binds to this.
}
public sealed override bool Equals(object symbol)
{
if ((object)this == symbol) return true;
......
......@@ -510,9 +510,9 @@ internal static string ReusableHoistedLocalFieldName(int number)
return "<>7__wrap" + StringExtensions.GetNumeral(number);
}
internal static string LambdaCopyParameterName(ParameterSymbol sourceParameter)
internal static string LambdaCopyParameterName(int ordinal)
{
return "<" + sourceParameter.Name + ">";
return "<p" + StringExtensions.GetNumeral(ordinal) + ">";
}
}
}
......@@ -1594,7 +1594,7 @@ public static void Main()
Assert.Null(GetSymbolNamesJoined(analysis.AlwaysAssigned));
Assert.Equal("p", GetSymbolNamesJoined(analysis.Captured));
Assert.Equal("i", GetSymbolNamesJoined(analysis.UnsafeAddressTaken));
Assert.Equal("<p>", GetSymbolNamesJoined(analysis.VariablesDeclared));
Assert.Equal("<p0>", GetSymbolNamesJoined(analysis.VariablesDeclared));
Assert.Equal("p", GetSymbolNamesJoined(analysis.DataFlowsIn));
Assert.Null(GetSymbolNamesJoined(analysis.DataFlowsOut));
......@@ -1602,7 +1602,7 @@ public static void Main()
Assert.Equal("p", GetSymbolNamesJoined(analysis.ReadInside));
Assert.Equal("i", GetSymbolNamesJoined(analysis.ReadOutside));
Assert.Equal("<p>", GetSymbolNamesJoined(analysis.WrittenInside));
Assert.Equal("<p0>", GetSymbolNamesJoined(analysis.WrittenInside));
Assert.Equal("i, p, d", GetSymbolNamesJoined(analysis.WrittenOutside));
}
......
......@@ -1638,8 +1638,8 @@ static void Main(string[] args)
var lambdaParameters = ((MethodSymbol)(model.GetSymbolInfo(node1)).Symbol).Parameters;
Assert.Equal("System.Object <sender>", lambdaParameters[0].ToTestDisplayString());
Assert.Equal("System.EventArgs <e>", lambdaParameters[1].ToTestDisplayString());
Assert.Equal("System.Object <p0>", lambdaParameters[0].ToTestDisplayString());
Assert.Equal("System.EventArgs <p1>", lambdaParameters[1].ToTestDisplayString());
CompileAndVerify(compilation);
}
......@@ -2292,7 +2292,7 @@ public static class C
}
[Fact, WorkItem(278481, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481")]
public void LambdaReturningNull()
public void LambdaReturningNull_1()
{
var src = @"
public static class ExtensionMethods
......@@ -2319,6 +2319,18 @@ public static class ExtensionMethods
return null;
}
public static System.Collections.Generic.IEnumerable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(
this System.Collections.Generic.IEnumerable<TOuter> outerValues,
System.Linq.IQueryable<TInner> innerValues,
System.Func<TOuter, TKey> outerKeySelector,
System.Func<TInner, TKey> innerKeySelector,
System.Func<TOuter, TInner, TResult> fullResultSelector,
System.Func<TOuter, TResult> partialResultSelector)
{
System.Console.WriteLine(""2"");
return null;
}
public static System.Collections.Generic.IEnumerable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(
this System.Collections.Generic.IEnumerable<TOuter> outerQueryable,
System.Collections.Generic.IEnumerable<TInner> innerQueryable,
......@@ -2356,6 +2368,36 @@ class B
CompileAndVerify(comp, expectedOutput: "1");
}
[Fact, WorkItem(296550, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=296550")]
public void LambdaReturningNull_2()
{
var src = @"
class Test1<T>
{
public void M1(System.Func<T> x) {}
public void M1<S>(System.Func<S> x) {}
public void M2<S>(System.Func<S> x) {}
public void M2(System.Func<T> x) {}
}
class Test2 : Test1<System.>
{
void Main()
{
M1(()=> null);
M2(()=> null);
}
}
";
var comp = CreateCompilationWithMscorlib(src, options: TestOptions.DebugDll);
comp.VerifyDiagnostics(
// (10,32): error CS1001: Identifier expected
// class Test2 : Test1<System.>
Diagnostic(ErrorCode.ERR_IdentifierExpected, ">").WithLocation(10, 32)
);
}
[Fact]
public void ThrowExpression_Lambda()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册