Skip to content

Commit

Permalink
Misc.
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Aug 24, 2021
1 parent 196e36d commit 069d047
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 159 deletions.
37 changes: 8 additions & 29 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Expand Up @@ -565,30 +565,18 @@ private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, Bou
Debug.Assert(syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType));
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var delegateType = unboundLambda.GetInferredDelegateType(ref useSiteInfo);
BoundLambda boundLambda;
bool hasErrors = false;
if (delegateType is { })
Debug.Assert(delegateType is { });
bool isExpressionTree = destination.IsNonGenericExpressionType();
if (isExpressionTree)
{
bool isExpressionTree = destination.IsNonGenericExpressionType();
if (isExpressionTree)
{
delegateType = Compilation.GetWellKnownType(WellKnownType.System_Linq_Expressions_Expression_T).Construct(delegateType);
delegateType.AddUseSiteInfo(ref useSiteInfo);
}
boundLambda = unboundLambda.Bind(delegateType, isExpressionTree);
}
else
{
hasErrors = true;
diagnostics.Add(ErrorCode.ERR_CannotInferDelegateType, syntax.GetLocation());
delegateType = CreateErrorType();
boundLambda = unboundLambda.BindForErrorRecovery();
delegateType = Compilation.GetWellKnownType(WellKnownType.System_Linq_Expressions_Expression_T).Construct(delegateType);
delegateType.AddUseSiteInfo(ref useSiteInfo);
}
var boundLambda = unboundLambda.Bind(delegateType, isExpressionTree);
diagnostics.AddRange(boundLambda.Diagnostics);
// PROTOTYPE: Should we create a ConversionGroup if conversionGroup is null?
var expr = createAnonymousFunctionConversion(syntax, source, boundLambda, Conversion.AnonymousFunction, isCast, conversionGroup, delegateType);
conversion = Conversions.ClassifyConversionFromExpression(expr, destination, ref useSiteInfo);
if (!conversion.Exists && !hasErrors)
if (!conversion.Exists)
{
GenerateImplicitConversionError(diagnostics, syntax, conversion, source, destination);
}
Expand Down Expand Up @@ -625,15 +613,7 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
Debug.Assert(syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType));
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var delegateType = source.GetInferredDelegateType(ref useSiteInfo);
if (delegateType is null)
{
diagnostics.Add(syntax, useSiteInfo);
// PROTOTYPE: Avoid reaching into the MemberAccessExpressionSyntax. The only
// reason we're doing that is to avoid updating various tests in DelegateTypeTests.
diagnostics.Add(ErrorCode.ERR_CannotInferDelegateType, (syntax is MemberAccessExpressionSyntax memberAccess ? memberAccess.Name : syntax).GetLocation());
return new BoundConversion(syntax, source, Conversion.NoConversion, @checked: false, explicitCastInCode: isCast, conversionGroup, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: true) { WasCompilerGenerated = source.WasCompilerGenerated };
}
// PROTOTYPE: Should we create a ConversionGroup if conversionGroup is null?
Debug.Assert(delegateType is { });
conversion = Conversions.ClassifyConversionFromExpression(source, delegateType, ref useSiteInfo);
BoundExpression expr;
if (!conversion.Exists)
Expand Down Expand Up @@ -834,7 +814,6 @@ private BoundMethodGroup FixMethodGroupWithTypeOrValue(BoundMethodGroup group, C
BoundExpression? receiverOpt = group.ReceiverOpt;
RoslynDebug.Assert(receiverOpt != null);

// PROTOTYPE: Test this with a FunctionType conversion. That is, test that we're reporting diagnostics when needed.
receiverOpt = ReplaceTypeOrValueReceiver(receiverOpt, useType: conversion.Method?.RequiresInstanceReceiver == false && !conversion.IsExtensionMethod, diagnostics);
return group.Update(
group.TypeArgumentsOpt,
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Expand Up @@ -3257,7 +3257,7 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
ImmutableArray<BoundExpression> boundInitializerExpressions = BindArrayInitializerExpressions(initializer, diagnostics, dimension: 1, rank: rank);

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
TypeSymbol bestType = MethodTypeInferrer.InferBestType(this, Conversions, constructedContainingType: /*PROTOTYPE:*/null, boundInitializerExpressions, ref useSiteInfo).Type;
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, ref useSiteInfo);
diagnostics.Add(node, useSiteInfo);

if ((object)bestType == null || bestType.IsVoidType()) // Dev10 also reports ERR_ImplicitlyTypedArrayNoBestType for void.
Expand Down
31 changes: 19 additions & 12 deletions src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
Expand Up @@ -44,13 +44,6 @@ public static NullableFlowState GetNullableState(ArrayBuilder<TypeWithState> typ
return result;
}

// PROTOTYPE: BestTypeInferrer.InferBestType(ImmutableArray<BoundExpression> exprs, ...)
// should use the expressions for calculating conversions, not the expression types. In short, the
// behavior should match method type inference, as the spec comment in InferBestType() says.

// PROTOTYPE: NullableWalker still uses this method to calculate a lambda return type.
// That should fail for examples such as BestCommonType_04.

/// <remarks>
/// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
/// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
Expand All @@ -75,7 +68,7 @@ public static NullableFlowState GetNullableState(ArrayBuilder<TypeWithState> typ
HashSet<TypeSymbol> candidateTypes = new HashSet<TypeSymbol>(comparer);
foreach (BoundExpression expr in exprs)
{
TypeSymbol? type = expr.Type;
TypeSymbol? type = expr.GetTypeOrSignature();

if (type is { })
{
Expand All @@ -93,6 +86,11 @@ public static NullableFlowState GetNullableState(ArrayBuilder<TypeWithState> typ
builder.AddRange(candidateTypes);
var result = GetBestType(builder, conversions, ref useSiteInfo);
builder.Free();

if (result is FunctionTypeSymbol functionType)
{
return functionType.GetInternalDelegateType();
}
return result;
}

Expand All @@ -115,10 +113,6 @@ public static NullableFlowState GetNullableState(ArrayBuilder<TypeWithState> typ
// SPEC: • If only one of x and y has a type, and both x and y, are implicitly convertible to that type, then that is the type of the conditional expression.
// SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs.

// PROTOTYPE: The code below does not execute the spec as above exactly. Instead it checks implicit conversions from expression to type:
// SPEC: o If an implicit conversion (§6.1) exists from _x_ to Y, but not from _y_ to X, then Y is the type of the conditional expression.
// SPEC: o If an implicit conversion (§6.1) exists from _y_ to X, but not from _x_ to Y, then X is the type of the conditional expression.

// A type is a candidate if all expressions are convertible to that type.
ArrayBuilder<TypeSymbol> candidateTypes = ArrayBuilder<TypeSymbol>.GetInstance();
try
Expand Down Expand Up @@ -252,6 +246,19 @@ public static NullableFlowState GetNullableState(ArrayBuilder<TypeWithState> typ
return type1;
}

// Prefer types other than FunctionTypeSymbol.
if (type1 is FunctionTypeSymbol)
{
if (!(type2 is FunctionTypeSymbol))
{
return type2;
}
}
else if (type2 is FunctionTypeSymbol)
{
return type1;
}

var conversionsWithoutNullability = conversions.WithNullability(false);
var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteInfo).Exists;
var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteInfo).Exists;
Expand Down
Expand Up @@ -282,36 +282,6 @@ private enum Dependency
return inferrer.InferTypeArgs(binder, ref useSiteInfo);
}

internal static TypeWithAnnotations InferBestType(
Binder binder,
ConversionsBase conversions,
NamedTypeSymbol constructedContainingType,
ImmutableArray<BoundExpression> arguments,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (!arguments.Any(arg => arg.Kind is BoundKind.UnboundLambda or BoundKind.MethodGroup))
{
return TypeWithAnnotations.Create(BestTypeInferrer.InferBestType(arguments, conversions, ref useSiteInfo));
}

var typeParameters = IndexedTypeParameterSymbol.TakeSymbols(count: 1);
var parameterType = TypeWithAnnotations.Create(typeParameters[0]);
var parameterTypes = arguments.SelectAsArray((_, i, _) => parameterType, (object)null);
var inferrer = new MethodTypeInferrer(
binder.Compilation,
conversions,
typeParameters,
constructedContainingType,
parameterTypes,
default(ImmutableArray<RefKind>),
arguments,
extensions: /*PROTOTYPE:*/null);
var result = inferrer.InferTypeArgs(binder, ref useSiteInfo);
return result.Success ?
result.InferredTypeArguments[0] :
default;
}

////////////////////////////////////////////////////////////////////////////////
//
// Fixed, unfixed and bounded type parameters
Expand Down
Expand Up @@ -1969,14 +1969,6 @@ private static ParameterSymbol GetParameter(int argIndex, MemberAnalysisResult r
}
}

// Prefer anonymous method and method group conversions where the target type is a strongly-typed delegate or expression.
result = PreferAnonymousMethodConversionsOverFunctionTypeConversions(arguments, m1, m2);
if (result != BetterResult.Neither)
{
Debug.Assert(result != BetterResult.Equal);
return result;
}

return PreferValOverInOrRefInterpolatedHandlerParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters);
}

Expand Down Expand Up @@ -2121,45 +2113,6 @@ private static ParameterSymbol GetParameter(int argIndex, MemberAnalysisResult r
return PreferValOverInOrRefInterpolatedHandlerParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters);
}

private static BetterResult PreferAnonymousMethodConversionsOverFunctionTypeConversions<TMember>(
ArrayBuilder<BoundExpression> arguments,
MemberResolutionResult<TMember> m1,
MemberResolutionResult<TMember> m2)
where TMember : Symbol
{
Debug.Assert(m1.IsValid);
Debug.Assert(m2.IsValid);

BetterResult result = BetterResult.Neither;

for (int i = 0; i < arguments.Count; ++i)
{
if (arguments[i].Kind == BoundKind.ArgListOperator)
{
continue;
}
switch (m1.Result.ConversionForArg(i).Kind, m2.Result.ConversionForArg(i).Kind)
{
case (ConversionKind.AnonymousFunction or ConversionKind.MethodGroup, ConversionKind.FunctionType):
if (result == BetterResult.Right)
{
return BetterResult.Neither;
}
result = BetterResult.Left;
break;
case (ConversionKind.FunctionType, ConversionKind.AnonymousFunction or ConversionKind.MethodGroup):
if (result == BetterResult.Left)
{
return BetterResult.Neither;
}
result = BetterResult.Right;
break;
}
}

return result;
}

private static BetterResult PreferValOverInOrRefInterpolatedHandlerParameters<TMember>(
ArrayBuilder<BoundExpression> arguments,
MemberResolutionResult<TMember> m1,
Expand Down Expand Up @@ -2497,16 +2450,6 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy
}
}

// PROTOTYPE: Is this change necessary? When is code above for interpolated string
// handler conversion used and the corresponding "better function member" code is not?
switch ((conv1.Kind, conv2.Kind))
{
case (ConversionKind.AnonymousFunction, ConversionKind.FunctionType):
return BetterResult.Left;
case (ConversionKind.FunctionType, ConversionKind.AnonymousFunction):
return BetterResult.Right;
}

// Given an implicit conversion C1 that converts from an expression E to a type T1,
// and an implicit conversion C2 that converts from an expression E to a type T2,
// C1 is a better conversion than C2 if E does not exactly match T2 and one of the following holds:
Expand Down
Expand Up @@ -103,6 +103,9 @@ public static bool HasDynamicType(this BoundExpression node)
return delegateType;
}

// PROTOTYPE: This method might return a FunctionTypeSymbol with null internal delegate type.
// There are various callers, such as MethodTypeInferrer and BestTypeInferrer, that are not
// checking for null delegate directly. Test those cases with delegate types that cannot be inferred.
public static TypeSymbol? GetTypeOrSignature(this BoundExpression expr)
{
if (expr.Type is { } type)
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Expand Up @@ -230,7 +230,8 @@ public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, N
}
else
{
bestResultType = MethodTypeInferrer.InferBestType(binder, conversions, constructedContainingType: /*PROTOTYPE:*/null, returns.SelectAsArray(pair => pair.Item1), ref useSiteInfo);
var bestType = BestTypeInferrer.InferBestType(returns.SelectAsArray(pair => pair.Item1), conversions, ref useSiteInfo);
bestResultType = bestType is null ? default : TypeWithAnnotations.Create(bestType);
}

if (!isAsync)
Expand Down
5 changes: 2 additions & 3 deletions src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
Expand Up @@ -3430,7 +3430,7 @@ private int GetOrCreatePlaceholderSlot(object identifier, TypeWithAnnotations ty
if (!node.HasErrors)
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bestType = MethodTypeInferrer.InferBestType(_binder, _conversions, constructedContainingType: /*PROTOTYPE:*/null, placeholders, ref discardedUseSiteInfo).Type;
bestType = BestTypeInferrer.InferBestType(placeholders, _conversions, ref discardedUseSiteInfo);
}

TypeWithAnnotations inferredType = (bestType is null)
Expand Down Expand Up @@ -3518,7 +3518,7 @@ private int GetOrCreatePlaceholderSlot(object identifier, TypeWithAnnotations ty

var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
var placeholders = placeholdersBuilder.ToImmutableAndFree();
TypeSymbol? bestType = BestTypeInferrer.InferBestType(placeholders, walker._conversions, ref discardedUseSiteInfo); // PROTOTYPE: Should use MethodTypeInferrer.InferBestType().
TypeSymbol? bestType = BestTypeInferrer.InferBestType(placeholders, walker._conversions, ref discardedUseSiteInfo);

TypeWithAnnotations inferredType;
if (bestType is { })
Expand Down Expand Up @@ -6974,7 +6974,6 @@ private bool HasTopLevelNullabilityConversion(TypeWithAnnotations source, TypeWi
{
NamedTypeSymbol { TypeKind: TypeKind.Delegate, DelegateInvokeMethod: { Parameters: { } parameters } signature } => (signature, parameters),
FunctionPointerTypeSymbol { Signature: { Parameters: { } parameters } signature } => (signature, parameters),
// PROTOTYPE: Can we check for Conversions.IsAssignableFromMulticastDelegate(targetType)?
_ => (null, ImmutableArray<ParameterSymbol>.Empty),
};

Expand Down

0 comments on commit 069d047

Please sign in to comment.