diff --git a/src/Compiler/Dynamic/Conversion.cs b/src/Compiler/Dynamic/Conversion.cs index fba239f4..36be7f7a 100644 --- a/src/Compiler/Dynamic/Conversion.cs +++ b/src/Compiler/Dynamic/Conversion.cs @@ -474,9 +474,9 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection } } // check for custom conversions - MemberInfo[] implicits = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, + MemberInfo[] implicitsOnFromType = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, "op_Implicit"); - foreach (MemberInfo member in implicits) + foreach (MemberInfo member in implicitsOnFromType) { MethodInfo method = (MethodInfo) member; if (method.ReturnType == toType) @@ -489,9 +489,25 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection return true; } } - MemberInfo[] explicits = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, + MemberInfo[] implicitsOnToType = toType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, + "op_Implicit"); + foreach (MemberInfo member in implicitsOnToType) + { + MethodInfo method = (MethodInfo)member; + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == fromType) + { + info.SubclassCount = 1000; + info.Converter = delegate (object value) + { + return Util.Invoke(method, null, value); + }; + return true; + } + } + MemberInfo[] explicitsOnFromType = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, "op_Explicit"); - foreach (MemberInfo member in explicits) + foreach (MemberInfo member in explicitsOnFromType) { MethodInfo method = (MethodInfo) member; if (method.ReturnType == toType) @@ -504,6 +520,23 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection return true; } } + MemberInfo[] explicitsOnToType = toType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName, + "op_Explicit"); + + foreach (MemberInfo member in explicitsOnToType) + { + MethodInfo method = (MethodInfo)member; + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == fromType) + { + info.IsExplicit = true; + info.Converter = delegate (object value) + { + return Util.Invoke(method, null, value); + }; + return true; + } + } // lastly try the IConvertible interface if (toType.IsPrimitive) { diff --git a/src/Csoft/EmbeddedResourceSourceProvider.cs b/src/Csoft/EmbeddedResourceSourceProvider.cs index e3dcbb56..9b045b73 100644 --- a/src/Csoft/EmbeddedResourceSourceProvider.cs +++ b/src/Csoft/EmbeddedResourceSourceProvider.cs @@ -4,10 +4,9 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,17 +16,9 @@ namespace Microsoft.ML.Probabilistic.Compiler { public SourceCode TryGetSource(Type t) { - var asm = t.Assembly; - return asm.GetManifestResourceNames().Where(s => s.EndsWith(".cs")).Select(s => + bool IsPrimaryFile(SourceFile sf) { - var stream = asm.GetManifestResourceStream(s); - using (var reader = new StreamReader(stream)) - { - return new SourceCode(s, reader.ReadToEnd()); - } - }).FirstOrDefault(code => - { - var tree = CSharpSyntaxTree.ParseText(code.SourceText, null, code.FilePath); + var tree = CSharpSyntaxTree.ParseText(sf.SourceText, null, sf.FilePath); var root = tree.GetRoot(); var typeDecl = root.DescendantNodes() .OfType() @@ -38,7 +29,24 @@ namespace Microsoft.ML.Probabilistic.Compiler .Where(md => md.Identifier.ValueText.Equals(t.Name)) .FirstOrDefault(); return typeDecl != null; - }); + } + var asm = t.Assembly; + SourceFile primaryFile = null; + List additionalFiles = new List(); + foreach (var s in asm.GetManifestResourceNames().Where(s => s.EndsWith(".cs"))) + { + SourceFile sf; + var stream = asm.GetManifestResourceStream(s); + using (var reader = new StreamReader(stream)) + { + sf = new SourceFile(s, reader.ReadToEnd()); + } + if (primaryFile == null && IsPrimaryFile(sf)) + primaryFile = sf; + else + additionalFiles.Add(sf); + } + return primaryFile == null ? null : new SourceCode(primaryFile, additionalFiles.ToImmutableArray()); } } } diff --git a/src/Csoft/ISourceProvider.cs b/src/Csoft/ISourceProvider.cs index 0b0ea55d..5cbdabbf 100644 --- a/src/Csoft/ISourceProvider.cs +++ b/src/Csoft/ISourceProvider.cs @@ -3,25 +3,41 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Immutable; namespace Microsoft.ML.Probabilistic.Compiler { - public class SourceCode + public class SourceFile { public string FilePath { get; } public string SourceText { get; } - public SourceCode(string filePath, string sourceText) + public SourceFile(string filePath, string sourceText) { FilePath = filePath; SourceText = sourceText; } } + public class SourceCode + { + /// + /// Source file that contains the primary definition. + /// + public SourceFile PrimaryFile { get; } + /// + /// Additional files that contain the definitions of some of the types referenced in the primary file. + /// Should be used to build a more complete semantic model. + /// + public ImmutableArray AddtionalFiles { get; } + + public SourceCode(SourceFile primaryFile, ImmutableArray additionalFiles) + { + PrimaryFile = primaryFile; + AddtionalFiles = additionalFiles; + } + } + public interface ISourceProvider { SourceCode TryGetSource(Type t); diff --git a/src/Csoft/RoslynDeclarationProvider.cs b/src/Csoft/RoslynDeclarationProvider.cs index 0baa9fe4..10bbe192 100644 --- a/src/Csoft/RoslynDeclarationProvider.cs +++ b/src/Csoft/RoslynDeclarationProvider.cs @@ -71,10 +71,12 @@ namespace Microsoft.ML.Probabilistic.Compiler var source = SourceProvider.TryGetSource(t); if (source == null) throw new InvalidOperationException($"Cannot find source code for the type {t.Name}"); - var tree = CSharpSyntaxTree.ParseText(source.SourceText, null, source.FilePath, Encoding.UTF8); + var primaryTree = CSharpSyntaxTree.ParseText(source.PrimaryFile.SourceText, null, source.PrimaryFile.FilePath, Encoding.UTF8); + var allTrees = source.AddtionalFiles.Select(f => CSharpSyntaxTree.ParseText(f.SourceText, null, f.FilePath, Encoding.UTF8)).ToList(); + allTrees.Add(primaryTree); var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - var targetAssemblyName = Path.GetFileNameWithoutExtension(tree.FilePath); + var targetAssemblyName = Path.GetFileNameWithoutExtension(primaryTree.FilePath); var asmName = t.Assembly.GetName(); var pk = asmName.GetPublicKey(); if (pk != null && pk.Length > 0) @@ -86,8 +88,8 @@ namespace Microsoft.ML.Probabilistic.Compiler .WithPublicSign(true) .WithCryptoPublicKey(System.Collections.Immutable.ImmutableArray.Create(pk)); } - var compilation = CSharpCompilation.Create(targetAssemblyName, new[] { tree }, references, options); - var model = compilation.GetSemanticModel(tree); + var compilation = CSharpCompilation.Create(targetAssemblyName, allTrees, references, options); + var model = compilation.GetSemanticModel(primaryTree); var errors = compilation.GetDiagnostics().ToList(); diff --git a/test/Tests/Dynamic/TypeInference.cs b/test/Tests/Dynamic/TypeInference.cs index eba75d46..1c409b99 100644 --- a/test/Tests/Dynamic/TypeInference.cs +++ b/test/Tests/Dynamic/TypeInference.cs @@ -143,6 +143,14 @@ namespace Microsoft.ML.Probabilistic.Tests Assert.True(Conversion.TryGetConversion(typeof (Tester), typeof (int[]), out conv)); o = conv.Converter(t); Console.WriteLine("{0}: {1}", o.GetType(), o); + Assert.True(Conversion.TryGetConversion(typeof(int), typeof(Tester), out conv)); + Assert.True(conv.IsExplicit); + Assert.True(Conversion.TryGetConversion(typeof(int[]), typeof(Tester), out conv)); + Assert.True(conv.IsExplicit); + Assert.True(Conversion.TryGetConversion(typeof(ImplicitlyConvertibleToTesterDefinesCast), typeof(Tester), out conv)); + Assert.False(conv.IsExplicit); + Assert.True(Conversion.TryGetConversion(typeof(ImplicitlyConvertibleToTesterCastDefinedOnTester), typeof(Tester), out conv)); + Assert.False(conv.IsExplicit); // conversion from null Assert.False(Conversion.TryGetConversion(typeof (Nullable), typeof (int), out conv)); @@ -842,8 +850,35 @@ namespace Microsoft.ML.Probabilistic.Tests { return t.arrayField; } + + public static explicit operator Tester(int x) + { + var tester = new Tester + { + intField = x + }; + return tester; + } + + public static explicit operator Tester(int[] x) + { + var tester = new Tester + { + arrayField = x + }; + return tester; + } + + public static implicit operator Tester(ImplicitlyConvertibleToTesterCastDefinedOnTester _) => new Tester(); } + public class ImplicitlyConvertibleToTesterDefinesCast + { + public static implicit operator Tester(ImplicitlyConvertibleToTesterDefinesCast _) => new Tester(); + } + + public class ImplicitlyConvertibleToTesterCastDefinedOnTester { } + /// /// Help class of dynamically invoking methods. /// diff --git a/test/Tests/MslTests.cs b/test/Tests/MslTests.cs index 0ca97b9b..47689598 100644 --- a/test/Tests/MslTests.cs +++ b/test/Tests/MslTests.cs @@ -70,7 +70,6 @@ namespace Microsoft.ML.Probabilistic.Tests [Fact] [Trait("Category", "CsoftModel")] - [Trait("Category", "OpenBug")] public void MethodInAnotherFileTest() { Assert.Throws(() =>