Skip to content

Commit

Permalink
Collection literals: type inference
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Jun 21, 2023
1 parent 6bd8524 commit bf36b25
Show file tree
Hide file tree
Showing 4 changed files with 765 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ protected BoundExpression ConvertForEachCollection(
return convertedCollectionExpression;
}

protected bool GetEnumeratorInfoAndInferCollectionElementType(
internal bool GetEnumeratorInfoAndInferCollectionElementType(
SyntaxNode syntax,
ExpressionSyntax collectionSyntax,
ref ForEachEnumeratorInfo.Builder builder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

Expand Down Expand Up @@ -558,7 +559,7 @@ private MethodTypeInferenceResult InferTypeArgs(Binder binder, ref CompoundUseSi
// SPEC: phase. The first phase makes some initial inferences of bounds, whereas
// SPEC: the second phase fixes type parameters to specific types and infers further
// SPEC: bounds. The second phase may have to be repeated a number of times.
InferTypeArgsFirstPhase(ref useSiteInfo);
InferTypeArgsFirstPhase(binder, ref useSiteInfo);
bool success = InferTypeArgsSecondPhase(binder, ref useSiteInfo);
var inferredTypeArguments = GetResults(out bool inferredFromFunctionType);
return new MethodTypeInferenceResult(success, inferredTypeArguments, inferredFromFunctionType);
Expand All @@ -569,7 +570,7 @@ private MethodTypeInferenceResult InferTypeArgs(Binder binder, ref CompoundUseSi
// The first phase
//

private void InferTypeArgsFirstPhase(ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
private void InferTypeArgsFirstPhase(Binder binder, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(!_formalParameterTypes.IsDefault);
Debug.Assert(!_arguments.IsDefault);
Expand All @@ -585,11 +586,12 @@ private void InferTypeArgsFirstPhase(ref CompoundUseSiteInfo<AssemblySymbol> use
TypeWithAnnotations target = _formalParameterTypes[arg];
ExactOrBoundsKind kind = GetRefKind(arg).IsManagedReference() || target.Type.IsPointerType() ? ExactOrBoundsKind.Exact : ExactOrBoundsKind.LowerBound;

MakeExplicitParameterTypeInferences(argument, target, kind, ref useSiteInfo);
MakeExplicitParameterTypeInferences(binder, argument, target, kind, ref useSiteInfo);
}
}

private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
#nullable enable
private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// SPEC: * If Ei is an anonymous function, and Ti is a delegate type or expression tree type,
// SPEC: an explicit type parameter inference is made from Ei to Ti and
Expand All @@ -609,8 +611,12 @@ private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeW
ExplicitParameterTypeInference(argument, target, ref useSiteInfo);
ExplicitReturnTypeInference(argument, target, ref useSiteInfo);
}
else if (argument.Kind == BoundKind.UnconvertedCollectionLiteralExpression)
{
MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)argument, target, ref useSiteInfo);
}
else if (argument.Kind != BoundKind.TupleLiteral ||
!MakeExplicitParameterTypeInferences((BoundTupleLiteral)argument, target, kind, ref useSiteInfo))
!MakeExplicitParameterTypeInferences(binder, (BoundTupleLiteral)argument, target, kind, ref useSiteInfo))
{
// Either the argument is not a tuple literal, or we were unable to do the inference from its elements, let's try to infer from argument type
var argumentType = _extensions.GetTypeWithAnnotations(argument);
Expand All @@ -626,7 +632,45 @@ private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeW
}
}

private bool MakeExplicitParameterTypeInferences(BoundTupleLiteral argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
private void MakeCollectionLiteralTypeInferences(Binder binder, BoundUnconvertedCollectionLiteralExpression argument, TypeWithAnnotations target, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (target.Type is null)
{
return;
}

if (argument.Elements.Length == 0)
{
return;
}

var builder = new ForEachEnumeratorInfo.Builder();
var targetCollectionSyntax = (ExpressionSyntax)argument.Syntax;
BoundExpression targetCollectionExpr = new BoundValuePlaceholder(targetCollectionSyntax, target.Type);
if (!binder.GetEnumeratorInfoAndInferCollectionElementType(targetCollectionSyntax, targetCollectionSyntax, ref builder, ref targetCollectionExpr, isAsync: false, BindingDiagnosticBag.Discarded, out TypeWithAnnotations targetElementType))
{
return;
}

foreach (var element in argument.Elements)
{
if (element.Kind == BoundKind.UnconvertedCollectionLiteralExpression)
{
MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)element, targetElementType, ref useSiteInfo);
}
else
{
var elementType = _extensions.GetTypeWithAnnotations(element); // PROTOTYPE: Test cases where _extensions returns something other than element.Type.
if (IsReallyAType(elementType.Type)) // PROTOTYPE: Test cases where IsReallyAType() returns false.
{
LowerBoundInference(elementType, targetElementType, ref useSiteInfo);
}
}
}
}
#nullable disable

private bool MakeExplicitParameterTypeInferences(Binder binder, BoundTupleLiteral argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// try match up element-wise to the destination tuple (or underlying type)
// Example:
Expand Down Expand Up @@ -659,7 +703,7 @@ private bool MakeExplicitParameterTypeInferences(BoundTupleLiteral argument, Typ
{
var sourceArgument = sourceArguments[i];
var destType = destTypes[i];
MakeExplicitParameterTypeInferences(sourceArgument, destType, kind, ref useSiteInfo);
MakeExplicitParameterTypeInferences(binder, sourceArgument, destType, kind, ref useSiteInfo);
}

return true;
Expand Down
4 changes: 3 additions & 1 deletion src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7228,9 +7228,11 @@ private static NullableAnnotation GetNullableAnnotation(BoundExpression expr)
case BoundKind.UnboundLambda:
case BoundKind.UnconvertedObjectCreationExpression:
case BoundKind.ConvertedTupleLiteral:
case BoundKind.UnconvertedCollectionLiteralExpression:
return NullableAnnotation.NotAnnotated;
default:
Debug.Assert(false); // unexpected value
// PROTOTYPE: Re-enable
//Debug.Assert(false); // unexpected value
return NullableAnnotation.Oblivious;
}
}
Expand Down
Loading

0 comments on commit bf36b25

Please sign in to comment.