Skip to content

Commit

Permalink
Added implicit functor conversions.
Browse files Browse the repository at this point in the history
  • Loading branch information
orthoxerox committed Dec 12, 2016
1 parent bcefcef commit cc4785e
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ internal partial class Binder
return CreateMethodGroupConversion(syntax, source, conversion, isCast, destination, diagnostics);
}

if (conversion.IsFunctor) {
return CreateFunctorConversion(syntax, source, conversion, isCast, destination, diagnostics);
}

if (conversion.IsAnonymousFunction && source.Kind == BoundKind.UnboundLambda)
{
return CreateAnonymousFunctionConversion(syntax, source, conversion, isCast, destination, diagnostics);
Expand Down Expand Up @@ -332,6 +336,27 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
return new BoundConversion(syntax, group, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: hasErrors) { WasCompilerGenerated = source.WasCompilerGenerated };
}

private BoundExpression CreateFunctorConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
{
var diag = new DiagnosticBag();

var invocation = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
(Syntax.ExpressionSyntax)source.Syntax,
SyntaxFactory.IdentifierName(WellKnownMemberNames.DelegateInvokeName));

var methodGroup = BindMethodGroup(
invocation,
invoked: true,
indexed: false,
diagnostics: diag) as BoundMethodGroup;

Debug.Assert(methodGroup != null);
Debug.Assert(!diag.HasAnyErrors());

return CreateMethodGroupConversion(invocation, methodGroup, conversion, isCast, destination, diagnostics);
}

private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
{
// We have a successful tuple conversion; rather than producing a separate conversion node
Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,10 @@ private static bool CheckNotNamespaceOrType(BoundExpression expr, DiagnosticBag
Error(diagnostics, ErrorCode.ERR_BadSKknown, expr.Syntax, ((BoundNamespaceExpression)expr).NamespaceSymbol, MessageID.IDS_SK_NAMESPACE.Localize(), MessageID.IDS_SK_VARIABLE.Localize());
return false;
case BoundKind.TypeExpression:
if (expr.Type.GetMembers(WellKnownMemberNames.DelegateInvokeName).Length != 0)
{
return true;
}
Error(diagnostics, ErrorCode.ERR_BadSKunknown, expr.Syntax, expr.Type, MessageID.IDS_SK_TYPE.Localize());
return false;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,18 @@ public bool IsMethodGroup
}
}

/// <summary>
/// Returns true if the conversion is an implicit functor conversion.
/// </summary>
/// <remarks>
/// Implicit functor conversions are not described in the C# language specification yet. TODO.
/// </remarks>
public bool IsFunctor {
get {
return Kind == ConversionKind.Functor;
}
}

/// <summary>
/// Returns true if the conversion is a pointer conversion
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal enum ConversionKind : byte
ImplicitUserDefined,
AnonymousFunction,
MethodGroup,
Functor,
ExplicitNumeric,
ExplicitEnumeration,
ExplicitNullable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind)
case ConversionKind.ImplicitUserDefined:
case ConversionKind.AnonymousFunction:
case ConversionKind.MethodGroup:
case ConversionKind.Functor:
case ConversionKind.PointerToVoid:
case ConversionKind.NullToPointer:
case ConversionKind.InterpolatedString:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,36 @@ public Conversion MethodGroupConversion(SyntaxNode syntax, MethodGroup methodGro
return conversion;
}

public override Conversion GetFunctorConversion(BoundExpression source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
var methods = source.Type.GetMembers(WellKnownMemberNames.DelegateInvokeName);
if (methods.Length == 0) {
return Conversion.NoConversion;
}

var diagnostics = new DiagnosticBag();

var methodGroup = _binder.BindExpression(SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
(ExpressionSyntax)source.Syntax,
SyntaxFactory.IdentifierName(WellKnownMemberNames.DelegateInvokeName)),
diagnostics) as BoundMethodGroup;

if (diagnostics.HasAnyErrors() || methodGroup == null) {
return Conversion.NoConversion;
}

var methodGroupConversion = GetMethodGroupConversion(methodGroup, destination, ref useSiteDiagnostics);

if (methodGroupConversion.Kind != ConversionKind.MethodGroup) {
return Conversion.NoConversion;
}

return new Conversion(ConversionKind.Functor, methodGroupConversion.Method, methodGroupConversion.IsExtensionMethod);

}


public static void GetDelegateArguments(SyntaxNode syntax, AnalyzedArguments analyzedArguments, ImmutableArray<ParameterSymbol> delegateParameters, CSharpCompilation compilation)
{
foreach (var p in delegateParameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth)

public abstract Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics);

public abstract Conversion GetFunctorConversion(BoundExpression source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics);

protected abstract ConversionsBase CreateInstance(int currentRecursionDepth);

protected abstract Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics);
Expand All @@ -35,6 +37,8 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth)

protected abstract Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool forCast);



internal AssemblySymbol CorLibrary { get { return corLibrary; } }

/// <summary>
Expand Down Expand Up @@ -840,6 +844,13 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi

case BoundKind.ThrowExpression:
return Conversion.ImplicitThrow;

default:
Conversion functorConversion = GetFunctorConversion(sourceExpression, destination, ref useSiteDiagnostics);
if (functorConversion.Exists) {
return functorConversion;
}
break;
}

return Conversion.NoConversion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public override Conversion GetMethodGroupConversion(BoundMethodGroup source, Typ
throw ExceptionUtilities.Unreachable;
}

public override Conversion GetFunctorConversion(BoundExpression source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// Conversions involving method groups require a Binder.
throw ExceptionUtilities.Unreachable;
}

protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// Conversions involving interpolated strings require a Binder.
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ private void EmitConversionExpression(BoundConversion conversion, bool used)
{
switch (conversion.ConversionKind)
{
case ConversionKind.Functor:
EmitMethodGroupConversion(conversion, used);
return;
case ConversionKind.MethodGroup:
EmitMethodGroupConversion(conversion, used);
return;
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body = null) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax
Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.CSharpParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion languageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.Default, Microsoft.CodeAnalysis.DocumentationMode documentationMode = Microsoft.CodeAnalysis.DocumentationMode.Parse, Microsoft.CodeAnalysis.SourceCodeKind kind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, System.Collections.Generic.IEnumerable<string> preprocessorSymbols = null) -> void
Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.SpecifiedLanguageVersion.get -> Microsoft.CodeAnalysis.CSharp.LanguageVersion
Microsoft.CodeAnalysis.CSharp.Conversion.IsFunctor.get -> bool
Microsoft.CodeAnalysis.CSharp.Conversion.IsThrow.get -> bool
Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleConversion.get -> bool
Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleLiteralConversion.get -> bool
Expand Down Expand Up @@ -129,8 +130,8 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithThrowKeyword(Micr
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Type.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.WithType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax.AddArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax
Expand Down
42 changes: 41 additions & 1 deletion src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3488,7 +3488,7 @@ static void Main()
}";
CompileAndVerify(source, expectedOutput: "42");
}

[Fact]
public void TestExtensionFunctor()
{
Expand All @@ -3511,5 +3511,45 @@ static void Main()
}";
CompileAndVerify(source, expectedOutput: "42", additionalRefs: new[] { SystemCoreRef });
}

[Fact]
public void TestFunctorAsDelegate()
{
const string source = @"
using System;
class C
{
string Invoke() => ""42"";
static void Foo(Func<string> func) => Console.Write(func());
static void Main()
{
var c = new C();
Foo(c);
}
}";
CompileAndVerify(source, expectedOutput: "42");
}

[Fact]
public void TestStaticFunctorAsDelegate()
{
const string source = @"
using System;
class C
{
static string Invoke() => ""42"";
static void Foo(Func<string> func) => Console.Write(func());
static void Main()
{
Foo(C);
}
}";
CompileAndVerify(source, expectedOutput: "42");
}

}
}

0 comments on commit cc4785e

Please sign in to comment.