From e63ad2f49908cec58ee5ad87aef55b1576a99398 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Fri, 30 Aug 2024 21:57:57 +0200 Subject: [PATCH] Improvements (#454) * BCL docs, crashfix * Added base types to system array * Added enum equality * Test fix * Update CompilingCodeTests.cs * Update TypeCheckingTests.cs * Update TypeCheckingTests.cs * Fixed syntax errors not bubbling up in scripts * Update ConstraintSolver_Rules.cs * Update TypeCheckingTests.cs * Added explicit impl detection * Update MetadataMethodSymbol.cs * Moved array into its own folder * Formatting fixes * One fix, one unfix * Update ArrayTypeSymbol.cs * Cleanup * Simplification, correctness * Update XmlDocumentationExtractor.cs --- src/Draco.Compiler.Cli/Program.cs | 4 +- .../EndToEnd/CompilingCodeTests.cs | 21 ++++ .../Scripting/ScriptTests.cs | 19 ++++ .../Semantics/DocumentationCommentsTests.cs | 18 ++-- .../Semantics/TypeCheckingTests.cs | 102 ++++++++++++++++++ src/Draco.Compiler.Tests/TestUtilities.cs | 2 +- src/Draco.Compiler/Api/MetadataReference.cs | 48 ++++++--- src/Draco.Compiler/Api/Syntax/SyntaxTree.cs | 2 +- .../Internal/Binding/BinderFacts.cs | 2 +- .../Internal/Binding/Binder_Expression.cs | 16 +-- .../Internal/Binding/Binder_Lvalue.cs | 5 +- .../Internal/BoundTree/BoundNode.cs | 2 +- .../Internal/Codegen/MetadataCodegen.cs | 2 +- .../Extractors/XmlDocumentationExtractor.cs | 21 +++- .../Solver/ConstraintSolver_Operations.cs | 1 + .../Internal/Solver/ConstraintSolver_Rules.cs | 20 ++-- .../Internal/Symbols/AliasSymbol.cs | 1 + .../Symbols/Error/ErrorMemberSymbol.cs | 1 + .../Symbols/Error/ErrorPropertySymbol.cs | 1 + .../Internal/Symbols/Error/ErrorTypeSymbol.cs | 2 +- .../Internal/Symbols/FieldSymbol.cs | 1 + .../Internal/Symbols/FunctionSymbol.cs | 1 + .../Symbols/Generic/PropertyInstanceSymbol.cs | 2 + .../Symbols/Generic/TypeInstanceSymbol.cs | 3 + .../Internal/Symbols/GlobalSymbol.cs | 1 + .../Internal/Symbols/IMemberSymbol.cs | 5 + .../Symbols/Metadata/MetadataMethodSymbol.cs | 7 ++ .../Metadata/MetadataPropertySymbol.cs | 3 + .../Symbols/Metadata/MetadataTypeSymbol.cs | 13 ++- .../Internal/Symbols/Metadata/TypeProvider.cs | 3 +- .../Internal/Symbols/ModuleSymbol.cs | 1 + .../Internal/Symbols/PropertySymbol.cs | 1 + .../Symbols/SymbolEqualityComparer.cs | 1 + .../{ => Array}/ArrayConstructorSymbol.cs | 4 +- .../{ => Array}/ArrayIndexPropertySymbol.cs | 3 +- .../Synthetized/Array/ArrayTypeSymbol.cs | 89 +++++++++++++++ .../Synthetized/ArrayLengthPropertySymbol.cs | 41 ------- .../Symbols/Synthetized/ArrayTypeSymbol.cs | 51 --------- .../Internal/Symbols/TypeSymbol.cs | 11 ++ .../Internal/Symbols/WellKnownTypes.cs | 23 +++- .../Internal/Symbols/WellKnownTypes.xml | 4 + .../DracoLanguageServer.cs | 2 +- src/Draco.ProjectSystem/Project.cs | 1 - src/Draco.Repl/ReplPromptCallbacks.cs | 1 - .../Templates/WellKnownTypes.sbncs | 4 +- 45 files changed, 407 insertions(+), 159 deletions(-) rename src/Draco.Compiler/Internal/Symbols/Synthetized/{ => Array}/ArrayConstructorSymbol.cs (95%) rename src/Draco.Compiler/Internal/Symbols/Synthetized/{ => Array}/ArrayIndexPropertySymbol.cs (96%) create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayTypeSymbol.cs delete mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayLengthPropertySymbol.cs delete mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayTypeSymbol.cs diff --git a/src/Draco.Compiler.Cli/Program.cs b/src/Draco.Compiler.Cli/Program.cs index 2a4900ce3..f9a56a57a 100644 --- a/src/Draco.Compiler.Cli/Program.cs +++ b/src/Draco.Compiler.Cli/Program.cs @@ -112,7 +112,7 @@ private static void CompileCommand(FileInfo[] input, FileInfo output, DirectoryI var compilation = Compilation.Create( syntaxTrees: syntaxTrees, metadataReferences: references - .Select(r => MetadataReference.FromPeStream(r.OpenRead())) + .Select(r => MetadataReference.FromFile(r.FullName)) .ToImmutableArray(), rootModulePath: rootModule?.FullName, outputPath: path, @@ -133,7 +133,7 @@ private static void RunCommand(FileInfo[] input, DirectoryInfo? rootModule, File var compilation = Compilation.Create( syntaxTrees: syntaxTrees, metadataReferences: references - .Select(r => MetadataReference.FromPeStream(r.OpenRead())) + .Select(r => MetadataReference.FromFile(r.FullName)) .ToImmutableArray(), rootModulePath: rootModule?.FullName); var execResult = Script.ExecuteAsProgram(compilation); diff --git a/src/Draco.Compiler.Tests/EndToEnd/CompilingCodeTests.cs b/src/Draco.Compiler.Tests/EndToEnd/CompilingCodeTests.cs index fb815ec9f..e1cc369a0 100644 --- a/src/Draco.Compiler.Tests/EndToEnd/CompilingCodeTests.cs +++ b/src/Draco.Compiler.Tests/EndToEnd/CompilingCodeTests.cs @@ -799,4 +799,25 @@ public void GlobalInferredFromBlock() Assert.Single(l); Assert.Equal(1, l[0]); } + + [Fact] + public void EnumEqualityOperators() + { + var assembly = Compile(""" + import System; + + public func equate(a: StringComparison, b: StringComparison): bool = a == b; + public func inequate(a: StringComparison, b: StringComparison): bool = a != b; + """); + + var eq1 = Invoke(assembly, "equate", StringComparison.Ordinal, StringComparison.Ordinal); + var eq2 = Invoke(assembly, "equate", StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase); + var neq1 = Invoke(assembly, "inequate", StringComparison.Ordinal, StringComparison.Ordinal); + var neq2 = Invoke(assembly, "inequate", StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase); + + Assert.True(eq1); + Assert.False(eq2); + Assert.False(neq1); + Assert.True(neq2); + } } diff --git a/src/Draco.Compiler.Tests/Scripting/ScriptTests.cs b/src/Draco.Compiler.Tests/Scripting/ScriptTests.cs index 7c4d06fd6..2f240cea7 100644 --- a/src/Draco.Compiler.Tests/Scripting/ScriptTests.cs +++ b/src/Draco.Compiler.Tests/Scripting/ScriptTests.cs @@ -27,4 +27,23 @@ public void BasicAssignmentAndAddition() Assert.True(result.Success); Assert.Equal(7, result.Value); } + + [Fact] + public void SyntaxErrorInScript() + { + // Arrange + var script = Script.Create(""" + var x = ; + """, + metadataReferences: Basic.Reference.Assemblies.Net80.ReferenceInfos.All + .Select(r => MetadataReference.FromPeStream(new MemoryStream(r.ImageBytes))) + .ToImmutableArray()); + + // Act + var result = script.Execute(); + + // Assert + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics); + } } diff --git a/src/Draco.Compiler.Tests/Semantics/DocumentationCommentsTests.cs b/src/Draco.Compiler.Tests/Semantics/DocumentationCommentsTests.cs index d5ff574ed..56f112969 100644 --- a/src/Draco.Compiler.Tests/Semantics/DocumentationCommentsTests.cs +++ b/src/Draco.Compiler.Tests/Semantics/DocumentationCommentsTests.cs @@ -214,7 +214,7 @@ public void TypeDocumentationFromMetadata() var testRef = CompileCSharpToMetadataRef($$""" /// {{docs}} public class TestClass { } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -258,7 +258,7 @@ public class TestClass /// {{docs}} public class NestedTestClass { } } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -304,7 +304,7 @@ public static class TestClass // Just so i can use it in draco public static int foo = 0; } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var @class = tree.FindInChildren(0).Accessed; @@ -348,7 +348,7 @@ public class TestClass /// {{docs}} public void TestMethod(int arg1, string arg2) { } } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -395,7 +395,7 @@ public class TestClass /// {{docs}} public void TestMethod() { } } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -440,7 +440,7 @@ public class TestClass /// {{docs}} public int TestField = 5; } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -485,7 +485,7 @@ public class TestClass /// {{docs}} public int TestProperty { get; } } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -534,7 +534,7 @@ public class TestClass /// {{methodDocs}} public void TestMethod(T arg1, T arg2, U arg3) { } } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); @@ -594,7 +594,7 @@ public class TestClass {{CreateXmlDocComment(originalDocs)}} public int TestMethod(int arg1, int arg2) => arg1 + arg2; } - """, xmlStream: xmlStream).WithDocumentation(xmlStream); + """, xmlStream: xmlStream); var call = tree.FindInChildren(0); diff --git a/src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs b/src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs index 050805de8..5766f0de7 100644 --- a/src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs +++ b/src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs @@ -2287,4 +2287,106 @@ public void InferredArrayElementTypeFromUsage() var intArray = compilation.WellKnownTypes.InstantiateArray(compilation.WellKnownTypes.SystemInt32); Assert.True(SymbolEqualityComparer.Default.Equals(intArray, aSym.Type)); } + + [Fact] + public void ArrayIndexResultHasTheRightType() + { + // func main() { + // val a = Array(3); + // val x = a[0]; + // } + + var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration( + "main", + ParameterList(), + null, + BlockFunctionBody( + DeclarationStatement(ImmutableVariableDeclaration( + "a", + null, + CallExpression(GenericExpression(NameExpression("Array"), NameType("int32")), LiteralExpression(3)))), + DeclarationStatement(ImmutableVariableDeclaration( + "x", + null, + IndexExpression(NameExpression("a"), LiteralExpression(0)))))))); + + // Act + var compilation = CreateCompilation(main); + var semanticModel = compilation.GetSemanticModel(main); + + var diags = semanticModel.Diagnostics; + var xDecl = main.FindInChildren(1); + var xSym = GetInternalSymbol(semanticModel.GetDeclaredSymbol(xDecl)); + + // Assert + Assert.Empty(diags); + Assert.False(xSym.IsError); + Assert.Equal(compilation.WellKnownTypes.SystemInt32, xSym.Type); + } + + [Fact] + public void ArrayIteratorVariableCorrectlyInferredInForLoop() + { + // func main() { + // val a = Array(3); + // for (x in a) { } + // } + + var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration( + "main", + ParameterList(), + null, + BlockFunctionBody( + DeclarationStatement(ImmutableVariableDeclaration( + "a", + null, + CallExpression(GenericExpression(NameExpression("Array"), NameType("int32")), LiteralExpression(3)))), + ExpressionStatement(ForExpression( + "x", + NameExpression("a"), + BlockExpression())))))); + + // Act + var compilation = CreateCompilation(main); + var semanticModel = compilation.GetSemanticModel(main); + + var diags = semanticModel.Diagnostics; + var xDecl = main.FindInChildren().Iterator; + var xSym = GetInternalSymbol(semanticModel.GetDeclaredSymbol(xDecl)); + + // Assert + Assert.Empty(diags); + Assert.False(xSym.IsError); + Assert.Equal(compilation.WellKnownTypes.SystemInt32, xSym.Type); + } + + [Fact] + public void ErrorExpressionsDoNotCascadeErrorsInOverloading() + { + // import System.Numerics; + // + // func foo(): Vector3 = Vector3() + Vector3(); + + var main = SyntaxTree.Create(CompilationUnit( + ImportDeclaration("System", "Numerics"), + FunctionDeclaration( + "foo", + ParameterList(), + NameType("Vector3"), + InlineFunctionBody(BinaryExpression( + CallExpression(NameExpression("Vector3")), + Plus, + CallExpression(NameExpression("Vector3"))))))); + + // Act + var compilation = CreateCompilation(main); + var semanticModel = compilation.GetSemanticModel(main); + var diags = semanticModel.Diagnostics; + + // Assert + Assert.Equal(2, diags.Length); + AssertDiagnostic(diags, TypeCheckingErrors.NoMatchingOverload); + + Assert.True(diags.All(d => !d.ToString().Contains("operator", StringComparison.OrdinalIgnoreCase))); + } } diff --git a/src/Draco.Compiler.Tests/TestUtilities.cs b/src/Draco.Compiler.Tests/TestUtilities.cs index 8e5bf3b84..6a9731a6e 100644 --- a/src/Draco.Compiler.Tests/TestUtilities.cs +++ b/src/Draco.Compiler.Tests/TestUtilities.cs @@ -15,7 +15,7 @@ internal static class TestUtilities public static MetadataReference CompileCSharpToMetadataRef(string code, string assemblyName = DefaultAssemblyName, IEnumerable? aditionalReferences = null, Stream? xmlStream = null) { var stream = CompileCSharpToStream(code, assemblyName, aditionalReferences, xmlStream); - return MetadataReference.FromPeStream(stream); + return MetadataReference.FromPeStream(stream, xmlStream); } public static Stream CompileCSharpToStream(string code, string assemblyName = DefaultAssemblyName, IEnumerable? aditionalReferences = null, Stream? xmlStream = null) diff --git a/src/Draco.Compiler/Api/MetadataReference.cs b/src/Draco.Compiler/Api/MetadataReference.cs index 3fc6afe29..1b326b34a 100644 --- a/src/Draco.Compiler/Api/MetadataReference.cs +++ b/src/Draco.Compiler/Api/MetadataReference.cs @@ -22,12 +22,28 @@ public abstract class MetadataReference /// public abstract XmlDocument? Documentation { get; } + /// + /// Creates a metadata reference reading up the given file. + /// + /// The path to the file to read. + /// The created from the file at . + public static MetadataReference FromFile(string path) + { + // NOTE: We could add a bool to turn off fetching the docs + var peStream = File.OpenRead(path); + // We assume the docs are under the same path, same name but with .xml extension + var docPath = Path.ChangeExtension(path, ".xml"); + var docStream = File.Exists(docPath) ? File.OpenRead(docPath) : null; + return FromPeStream(peStream, docStream); + } + /// /// Creates a metadata reference from the given assembly. /// /// The assembly to create a metadata reader from. + /// The XML documentation for the assembly. /// The created from . - public static MetadataReference FromAssembly(Assembly assembly) + public static MetadataReference FromAssembly(Assembly assembly, XmlDocument? documentation = null) { unsafe { @@ -37,7 +53,7 @@ public static MetadataReference FromAssembly(Assembly assembly) } var reader = new MetadataReader(blob, length); - return new MetadataReaderReference(reader); + return new MetadataReaderReference(reader, documentation); } } @@ -45,29 +61,37 @@ public static MetadataReference FromAssembly(Assembly assembly) /// Creates a metadata reference from the given PE stream. /// /// The PE stream to create the metadata reference from. + /// The XML documentation for the assembly. /// The reading up from . - public static MetadataReference FromPeStream(Stream peStream) + public static MetadataReference FromPeStream(Stream peStream, XmlDocument? documentation = null) { var peReader = new PEReader(peStream); var metadataReader = peReader.GetMetadataReader(); - return new MetadataReaderReference(metadataReader); + return new MetadataReaderReference(metadataReader, documentation); } /// - /// Adds xml documentation to this metadata reference. + /// Creates a metadata reference from the given PE stream. /// - /// The stream with the xml documentation. - /// New metadata reference containing xml documentation. - public MetadataReference WithDocumentation(Stream xmlStream) + /// The PE stream to create the metadata reference from. + /// The stream to read up XML documentation from for the assembly. + /// The reading up from . + public static MetadataReference FromPeStream(Stream peStream, Stream? xmlDocStream) { - var doc = new XmlDocument(); - doc.Load(xmlStream); - return new MetadataReaderReference(this.MetadataReader, doc); + var peReader = new PEReader(peStream); + var metadataReader = peReader.GetMetadataReader(); + var docXml = null as XmlDocument; + if (xmlDocStream is not null) + { + docXml = new XmlDocument(); + docXml.Load(xmlDocStream); + } + return new MetadataReaderReference(metadataReader, docXml); } private sealed class MetadataReaderReference( MetadataReader metadataReader, - XmlDocument? documentation = null) : MetadataReference + XmlDocument? documentation) : MetadataReference { public override MetadataReader MetadataReader { get; } = metadataReader; public override XmlDocument? Documentation { get; } = documentation; diff --git a/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs b/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs index 3842c2bdd..ee7b31d85 100644 --- a/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs +++ b/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs @@ -75,7 +75,7 @@ internal static SyntaxTree ParseScript(ISourceReader sourceReader) // Parse a repl entry var node = parser.ParseScriptEntry(); // Make it into a tree - return Create(node); + return Create(node, syntaxDiagnostics: syntaxDiagnostics); } /// diff --git a/src/Draco.Compiler/Internal/Binding/BinderFacts.cs b/src/Draco.Compiler/Internal/Binding/BinderFacts.cs index bfcf229de..c16c1aae9 100644 --- a/src/Draco.Compiler/Internal/Binding/BinderFacts.cs +++ b/src/Draco.Compiler/Internal/Binding/BinderFacts.cs @@ -3,7 +3,7 @@ using System.Text; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Symbols; -using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Binding; diff --git a/src/Draco.Compiler/Internal/Binding/Binder_Expression.cs b/src/Draco.Compiler/Internal/Binding/Binder_Expression.cs index f78f72b0b..70ea2f6ff 100644 --- a/src/Draco.Compiler/Internal/Binding/Binder_Expression.cs +++ b/src/Draco.Compiler/Internal/Binding/Binder_Expression.cs @@ -13,6 +13,7 @@ using Draco.Compiler.Internal.Symbols; using Draco.Compiler.Internal.Symbols.Error; using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Binding; @@ -634,16 +635,8 @@ private async BindingTask BindMemberExpression(MemberExpression case FieldSymbol field: return new BoundFieldExpression(syntax, receiver, field); case PropertySymbol prop: - // It could be array length - if (prop.GenericDefinition is ArrayLengthPropertySymbol) - { - return new BoundArrayLengthExpression(syntax, receiver); - } - else - { - var getter = GetGetterSymbol(syntax, prop, diagnostics); - return new BoundPropertyGetExpression(syntax, receiver, getter); - } + var getter = GetGetterSymbol(syntax, prop, diagnostics); + return new BoundPropertyGetExpression(syntax, receiver, getter); default: // TODO throw new NotImplementedException(); @@ -672,8 +665,7 @@ private async BindingTask BindIndexExpression(IndexExpressionSy var receiver = await receiverTask; var indexer = await indexerTask; - var arrayIndexProperty = (indexer.GenericDefinition as IPropertyAccessorSymbol)?.Property as ArrayIndexPropertySymbol; - if (arrayIndexProperty is not null) + if (receiver.TypeRequired.IsArrayType) { // Array getter return new BoundArrayAccessExpression(syntax, receiver, await BindingTask.WhenAll(argsTask)); diff --git a/src/Draco.Compiler/Internal/Binding/Binder_Lvalue.cs b/src/Draco.Compiler/Internal/Binding/Binder_Lvalue.cs index cd99896da..2c6818e92 100644 --- a/src/Draco.Compiler/Internal/Binding/Binder_Lvalue.cs +++ b/src/Draco.Compiler/Internal/Binding/Binder_Lvalue.cs @@ -8,7 +8,7 @@ using Draco.Compiler.Internal.Diagnostics; using Draco.Compiler.Internal.Solver; using Draco.Compiler.Internal.Symbols; -using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Binding; @@ -153,8 +153,7 @@ private async BindingTask BindIndexLvalue(IndexExpressionSyntax syn var receiver = await receiverTask; var indexer = await indexerTask; - var arrayIndexProperty = (indexer.GenericDefinition as IPropertyAccessorSymbol)?.Property as ArrayIndexPropertySymbol; - if (arrayIndexProperty is not null) + if (receiver.TypeRequired.IsArrayType) { return new BoundArrayAccessLvalue( syntax, diff --git a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs index c0ef307d3..ec2791867 100644 --- a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs +++ b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs @@ -2,7 +2,7 @@ using System.Linq; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Symbols; -using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.BoundTree; diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index e0cfa9b6b..28fac7004 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -14,7 +14,7 @@ using Draco.Compiler.Internal.Symbols.Metadata; using Draco.Compiler.Internal.Symbols.Script; using Draco.Compiler.Internal.Symbols.Source; -using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Codegen; diff --git a/src/Draco.Compiler/Internal/Documentation/Extractors/XmlDocumentationExtractor.cs b/src/Draco.Compiler/Internal/Documentation/Extractors/XmlDocumentationExtractor.cs index f7a292838..d0ca74533 100644 --- a/src/Draco.Compiler/Internal/Documentation/Extractors/XmlDocumentationExtractor.cs +++ b/src/Draco.Compiler/Internal/Documentation/Extractors/XmlDocumentationExtractor.cs @@ -93,13 +93,24 @@ private ImmutableArray ExtractElementsFromNode(XmlNode nod return elements.ToImmutable(); } - private ReferenceDocumentationElement ConstructReference(XmlNode node) + private DocumentationElement ConstructReference(XmlNode node) { + // It can be a 'cref' or a 'langword' var cref = node.Attributes?["cref"]?.Value; - var symbol = this.GetSymbolFromDocumentationName(cref ?? string.Empty) - // NOTE: The first two characters of the link is the documentation prefix - ?? new PrimitiveTypeSymbol(cref?[2..] ?? string.Empty, false); - return new ReferenceDocumentationElement(symbol, string.IsNullOrEmpty(node.InnerText) ? null : node.InnerText); + if (cref is not null) + { + var symbol = this.GetSymbolFromDocumentationName(cref) + // NOTE: The first two characters of the link is the documentation prefix + ?? new PrimitiveTypeSymbol(cref[2..], false); + return new ReferenceDocumentationElement(symbol, node.InnerText.Length == 0 ? null : node.InnerText); + } + var langword = node.Attributes?["langword"]?.Value; + if (langword is not null) + { + return new TextDocumentationElement(langword); + } + // Bail out with some default + return new TextDocumentationElement($"unknown reference element: {node.InnerText}"); } private Symbol? GetSymbolFromDocumentationName(string documentationName) => diff --git a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Operations.cs b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Operations.cs index b3c9c5bd1..f596e89de 100644 --- a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Operations.cs +++ b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Operations.cs @@ -4,6 +4,7 @@ using Draco.Compiler.Internal.Symbols; using Draco.Compiler.Internal.Symbols.Error; using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Solver; diff --git a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Rules.cs b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Rules.cs index e93f4227e..a8aee263e 100644 --- a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Rules.cs +++ b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Rules.cs @@ -293,10 +293,14 @@ private IEnumerable ConstructRules(DiagnosticBag diagnostics) => [ UnifyAsserted(overload.ReturnType, WellKnownTypes.ErrorType); // Best-effort shape approximation var errorSymbol = new ErrorFunctionSymbol(overload.Candidates.Arguments.Length); - overload.ReportDiagnostic(diagnostics, diag => diag - .WithTemplate(TypeCheckingErrors.NoMatchingOverload) - .WithFormatArgs(overload.FunctionName)); overload.CompletionSource.SetResult(errorSymbol); + // NOTE: If the arguments have an error, we don't report an error here to not cascade errors + if (overload.Candidates.Arguments.All(a => !a.Type.Substitution.IsError)) + { + overload.ReportDiagnostic(diagnostics, diag => diag + .WithTemplate(TypeCheckingErrors.NoMatchingOverload) + .WithFormatArgs(overload.FunctionName)); + } return; } @@ -306,10 +310,14 @@ private IEnumerable ConstructRules(DiagnosticBag diagnostics) => [ // Best-effort shape approximation UnifyAsserted(overload.ReturnType, WellKnownTypes.ErrorType); var errorSymbol = new ErrorFunctionSymbol(overload.Candidates.Arguments.Length); - overload.ReportDiagnostic(diagnostics, diag => diag - .WithTemplate(TypeCheckingErrors.AmbiguousOverloadedCall) - .WithFormatArgs(overload.FunctionName, string.Join(", ", overload.Candidates))); overload.CompletionSource.SetResult(errorSymbol); + // NOTE: If the arguments have an error, we don't report an error here to not cascade errors + if (overload.Candidates.Arguments.All(a => !a.Type.Substitution.IsError)) + { + overload.ReportDiagnostic(diagnostics, diag => diag + .WithTemplate(TypeCheckingErrors.AmbiguousOverloadedCall) + .WithFormatArgs(overload.FunctionName, string.Join(", ", overload.Candidates))); + } return; } diff --git a/src/Draco.Compiler/Internal/Symbols/AliasSymbol.cs b/src/Draco.Compiler/Internal/Symbols/AliasSymbol.cs index 45c9eb5c1..af614b4cc 100644 --- a/src/Draco.Compiler/Internal/Symbols/AliasSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/AliasSymbol.cs @@ -8,6 +8,7 @@ namespace Draco.Compiler.Internal.Symbols; internal abstract class AliasSymbol : Symbol, IMemberSymbol { public bool IsStatic => true; + public bool IsExplicitImplementation => false; /// /// The symbol being aliased. diff --git a/src/Draco.Compiler/Internal/Symbols/Error/ErrorMemberSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Error/ErrorMemberSymbol.cs index 51e3534de..b8dbef9e6 100644 --- a/src/Draco.Compiler/Internal/Symbols/Error/ErrorMemberSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Error/ErrorMemberSymbol.cs @@ -15,6 +15,7 @@ internal sealed class ErrorMemberSymbol : Symbol, ITypedSymbol, IMemberSymbol public TypeSymbol Type => WellKnownTypes.ErrorType; public bool IsStatic => true; + public bool IsExplicitImplementation => false; private ErrorMemberSymbol() { diff --git a/src/Draco.Compiler/Internal/Symbols/Error/ErrorPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Error/ErrorPropertySymbol.cs index e141bec2e..afe543719 100644 --- a/src/Draco.Compiler/Internal/Symbols/Error/ErrorPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Error/ErrorPropertySymbol.cs @@ -13,6 +13,7 @@ public static FunctionSymbol CreateIndexerSet(int indicesCount) => public override bool IsError => true; public override bool IsStatic => true; + public override bool IsExplicitImplementation => false; public override bool IsIndexer => false; public override TypeSymbol Type => WellKnownTypes.ErrorType; diff --git a/src/Draco.Compiler/Internal/Symbols/Error/ErrorTypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Error/ErrorTypeSymbol.cs index ee0b8cb8e..f3a63455c 100644 --- a/src/Draco.Compiler/Internal/Symbols/Error/ErrorTypeSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Error/ErrorTypeSymbol.cs @@ -1,5 +1,5 @@ -using Draco.Compiler.Internal.Symbols.Generic; using System.Collections.Immutable; +using Draco.Compiler.Internal.Symbols.Generic; namespace Draco.Compiler.Internal.Symbols.Error; diff --git a/src/Draco.Compiler/Internal/Symbols/FieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/FieldSymbol.cs index d309176ae..643c48412 100644 --- a/src/Draco.Compiler/Internal/Symbols/FieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/FieldSymbol.cs @@ -10,6 +10,7 @@ namespace Draco.Compiler.Internal.Symbols; internal abstract class FieldSymbol : VariableSymbol, IMemberSymbol { public bool IsStatic => false; + public bool IsExplicitImplementation => false; // NOTE: Override for covariant return type public override FieldSymbol? GenericDefinition => null; diff --git a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs index 00b80b1b3..beda51cfb 100644 --- a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs @@ -85,6 +85,7 @@ public delegate IOperand CodegenDelegate( public abstract TypeSymbol ReturnType { get; } public virtual bool IsStatic => true; + public virtual bool IsExplicitImplementation => false; /// /// If true, this is a virtual function. diff --git a/src/Draco.Compiler/Internal/Symbols/Generic/PropertyInstanceSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Generic/PropertyInstanceSymbol.cs index ca58929ea..1168e9b5a 100644 --- a/src/Draco.Compiler/Internal/Symbols/Generic/PropertyInstanceSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Generic/PropertyInstanceSymbol.cs @@ -26,6 +26,8 @@ internal sealed class PropertyInstanceSymbol( public override bool IsStatic => this.GenericDefinition.IsStatic; + public override bool IsExplicitImplementation => this.GenericDefinition.IsExplicitImplementation; + public override Symbol? ContainingSymbol { get; } = containingSymbol; public override PropertySymbol GenericDefinition { get; } = genericDefinition; diff --git a/src/Draco.Compiler/Internal/Symbols/Generic/TypeInstanceSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Generic/TypeInstanceSymbol.cs index dc92c09d7..66895084d 100644 --- a/src/Draco.Compiler/Internal/Symbols/Generic/TypeInstanceSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Generic/TypeInstanceSymbol.cs @@ -51,10 +51,13 @@ public override ImmutableArray GenericArguments public override ImmutableArray ImmediateBaseTypes => this.GenericDefinition.ImmediateBaseTypes .Select(x => x.GenericInstantiate(x.ContainingSymbol, this.Context)) .ToImmutableArray(); + public override bool IsAbstract => this.GenericDefinition.IsAbstract; public override bool IsTypeVariable => this.GenericDefinition.IsTypeVariable; public override bool IsValueType => this.GenericDefinition.IsValueType; public override bool IsDelegateType => this.GenericDefinition.IsDelegateType; public override bool IsInterface => this.GenericDefinition.IsInterface; + public override bool IsArrayType => this.GenericDefinition.IsArrayType; + public override bool IsSealed => this.GenericDefinition.IsSealed; public override string Name => this.GenericDefinition.Name; public override Symbol? ContainingSymbol { get; } = containingSymbol; diff --git a/src/Draco.Compiler/Internal/Symbols/GlobalSymbol.cs b/src/Draco.Compiler/Internal/Symbols/GlobalSymbol.cs index cfbc261f5..fc65757cc 100644 --- a/src/Draco.Compiler/Internal/Symbols/GlobalSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/GlobalSymbol.cs @@ -10,6 +10,7 @@ namespace Draco.Compiler.Internal.Symbols; internal abstract partial class GlobalSymbol : VariableSymbol, IMemberSymbol { public bool IsStatic => true; + public bool IsExplicitImplementation => false; // NOTE: Override for covariant return type public override GlobalSymbol? GenericDefinition => null; diff --git a/src/Draco.Compiler/Internal/Symbols/IMemberSymbol.cs b/src/Draco.Compiler/Internal/Symbols/IMemberSymbol.cs index c3bb29097..9dede0337 100644 --- a/src/Draco.Compiler/Internal/Symbols/IMemberSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/IMemberSymbol.cs @@ -9,4 +9,9 @@ internal interface IMemberSymbol /// Specifying if given symbol is static. /// public bool IsStatic { get; } + + /// + /// True if this member is an explicit implementation of an interface member. + /// + public bool IsExplicitImplementation { get; } } diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs index b4885d6fb..9213dfb52 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs @@ -62,7 +62,14 @@ public override bool IsVirtual || this.Override is not null; } } + public override bool IsStatic => methodDefinition.Attributes.HasFlag(MethodAttributes.Static); + + // TODO: Very hacky way of doing this + public override bool IsExplicitImplementation => + this.Visibility == Api.Semantics.Visibility.Private + || this.Name.Contains('.'); + public override Api.Semantics.Visibility Visibility { get diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataPropertySymbol.cs index d8e0abd93..0eb165fb2 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataPropertySymbol.cs @@ -25,6 +25,9 @@ internal sealed class MetadataPropertySymbol( private FunctionSymbol? setter; public override bool IsStatic => (this.Getter ?? this.Setter)?.IsStatic ?? throw new InvalidOperationException(); + public override bool IsExplicitImplementation => this.Getter?.IsExplicitImplementation + ?? this.Setter?.IsExplicitImplementation + ?? false; public override Api.Semantics.Visibility Visibility => (this.Getter ?? this.Setter)?.Visibility ?? throw new InvalidOperationException(); diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs index 92a34f709..764fd626e 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs @@ -40,7 +40,7 @@ internal sealed class MetadataTypeSymbol( public override Symbol ContainingSymbol { get; } = containingSymbol; - public override bool IsValueType => this.BaseTypes.Contains( + public override bool IsValueType => this.IsEnumType || this.BaseTypes.Contains( this.Assembly.Compilation.WellKnownTypes.SystemValueType, SymbolEqualityComparer.Default); @@ -48,6 +48,10 @@ internal sealed class MetadataTypeSymbol( this.Assembly.Compilation.WellKnownTypes.SystemDelegate, SymbolEqualityComparer.Default); + public override bool IsEnumType => this.BaseTypes.Contains( + this.Assembly.Compilation.WellKnownTypes.SystemEnum, + SymbolEqualityComparer.Default); + public override bool IsInterface => typeDefinition.Attributes.HasFlag(TypeAttributes.Interface); public override bool IsAbstract => typeDefinition.Attributes.HasFlag(TypeAttributes.Abstract); @@ -188,6 +192,13 @@ private ImmutableArray BuildMembers() if (propSym.Visibility == Api.Semantics.Visibility.Public) result.Add(propSym); } + // If this is an enum, inject == and != operators + if (this.IsEnumType) + { + var wellKnownTypes = this.Assembly.Compilation.WellKnownTypes; + result.AddRange(wellKnownTypes.GetEnumEqualityMembers(this)); + } + // Done return result.ToImmutable(); } diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs index 23fa996e9..241717352 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs @@ -9,6 +9,7 @@ using Draco.Compiler.Api.Diagnostics; using Draco.Compiler.Internal.Binding; using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Symbols.Metadata; @@ -41,7 +42,7 @@ private readonly record struct CacheKey( private readonly ConcurrentDictionary cache = new(); public TypeSymbol GetArrayType(TypeSymbol elementType, ArrayShape shape) => - new ArrayTypeSymbol(shape.Rank, this.WellKnownTypes.SystemInt32).GenericInstantiate(elementType); + new ArrayTypeSymbol(compilation, shape.Rank, this.WellKnownTypes.SystemInt32).GenericInstantiate(elementType); public TypeSymbol GetSZArrayType(TypeSymbol elementType) => this.WellKnownTypes.ArrayType.GenericInstantiate(elementType); public TypeSymbol GetByReferenceType(TypeSymbol elementType) => UnknownType; diff --git a/src/Draco.Compiler/Internal/Symbols/ModuleSymbol.cs b/src/Draco.Compiler/Internal/Symbols/ModuleSymbol.cs index 15077cfa2..c8352c2ea 100644 --- a/src/Draco.Compiler/Internal/Symbols/ModuleSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/ModuleSymbol.cs @@ -15,6 +15,7 @@ internal abstract partial class ModuleSymbol : Symbol, IMemberSymbol : Visibility.Internal; public bool IsStatic => true; + public bool IsExplicitImplementation => false; public override void Accept(SymbolVisitor visitor) => visitor.VisitModule(this); public override TResult Accept(SymbolVisitor visitor) => visitor.VisitModule(this); diff --git a/src/Draco.Compiler/Internal/Symbols/PropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/PropertySymbol.cs index d580d4a11..1eb24f74c 100644 --- a/src/Draco.Compiler/Internal/Symbols/PropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/PropertySymbol.cs @@ -27,6 +27,7 @@ internal abstract class PropertySymbol : Symbol, ITypedSymbol, IMemberSymbol, IO /// public abstract bool IsIndexer { get; } public abstract bool IsStatic { get; } + public abstract bool IsExplicitImplementation { get; } public virtual Symbol? Override => null; diff --git a/src/Draco.Compiler/Internal/Symbols/SymbolEqualityComparer.cs b/src/Draco.Compiler/Internal/Symbols/SymbolEqualityComparer.cs index 2bae80d0d..21b7e1967 100644 --- a/src/Draco.Compiler/Internal/Symbols/SymbolEqualityComparer.cs +++ b/src/Draco.Compiler/Internal/Symbols/SymbolEqualityComparer.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using Draco.Compiler.Internal.Symbols.Error; using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; namespace Draco.Compiler.Internal.Symbols; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayConstructorSymbol.cs similarity index 95% rename from src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs rename to src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayConstructorSymbol.cs index e749273e9..944fde8ef 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayConstructorSymbol.cs @@ -3,7 +3,7 @@ using System.Threading; using static Draco.Compiler.Internal.OptimizingIr.InstructionFactory; -namespace Draco.Compiler.Internal.Symbols.Synthetized; +namespace Draco.Compiler.Internal.Symbols.Synthetized.Array; /// /// A global constructor for arrays. @@ -48,7 +48,7 @@ internal sealed class ArrayConstructorSymbol(ArrayTypeSymbol genericArrayType) : private ImmutableArray BuildParameters() => this.Rank switch { - 1 => [new SynthetizedParameterSymbol(this, "capacity", genericArrayType.IndexType) as ParameterSymbol], + 1 => [new SynthetizedParameterSymbol(this, "capacity", genericArrayType.IndexType)], int n => Enumerable .Range(1, n) .Select(i => new SynthetizedParameterSymbol(this, $"capacity{i}", genericArrayType.IndexType) as ParameterSymbol) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayIndexPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayIndexPropertySymbol.cs similarity index 96% rename from src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayIndexPropertySymbol.cs rename to src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayIndexPropertySymbol.cs index a205fb647..ce6947a21 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayIndexPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayIndexPropertySymbol.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using System.Linq; -namespace Draco.Compiler.Internal.Symbols.Synthetized; +namespace Draco.Compiler.Internal.Symbols.Synthetized.Array; /// /// The indexer property of arrays. @@ -15,6 +15,7 @@ internal sealed class ArrayIndexPropertySymbol : PropertySymbol public override bool IsIndexer => true; public override bool IsStatic => false; + public override bool IsExplicitImplementation => false; public ArrayIndexPropertySymbol(ArrayTypeSymbol containingSymbol) { diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayTypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayTypeSymbol.cs new file mode 100644 index 000000000..47924ec59 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/Array/ArrayTypeSymbol.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Draco.Compiler.Api; + +namespace Draco.Compiler.Internal.Symbols.Synthetized.Array; + +/// +/// An array modeled as generics. +/// +internal sealed class ArrayTypeSymbol : TypeSymbol +{ + /// + /// The element type this array stores. + /// + public TypeParameterSymbol ElementType { get; } + + /// + /// The index type this array can be indexed with. + /// + public TypeSymbol IndexType { get; } + + /// + /// The rank of the array (number of dimensions). + /// + public int Rank { get; } + + public override bool IsArrayType => true; + + public override Compilation DeclaringCompilation { get; } + + public override ImmutableArray ImmediateBaseTypes => + InterlockedUtils.InitializeDefault(ref this.immediateBaseTypes, this.BuildImmediateBaseTypes); + private ImmutableArray immediateBaseTypes; + + public override string Name => this.Rank switch + { + 1 => "Array", + int n => $"Array{n}D", + }; + + public override ImmutableArray GenericParameters => [this.ElementType]; + + public override IEnumerable DefinedMembers => InterlockedUtils.InitializeDefault(ref this.definedMembers, this.BuildDefinedMembers); + private ImmutableArray definedMembers; + + public ArrayTypeSymbol(Compilation compilation, int rank, TypeSymbol indexType) + { + this.DeclaringCompilation = compilation; + this.ElementType = new SynthetizedTypeParameterSymbol(this, "T"); + this.Rank = rank; + this.IndexType = indexType; + } + + public TypeSymbol GenericInstantiate(TypeSymbol elementType) => + this.GenericInstantiate(containingSymbol: null, ImmutableArray.Create(elementType)); + + public override string ToString() => $"{this.Name}{this.GenericsToString()}"; + + private ImmutableArray BuildImmediateBaseTypes() + { + var wellKnownTypes = this.DeclaringCompilation.WellKnownTypes; + + // We need to implement System.Array, IList, IReadOnlyList + return [ + wellKnownTypes.SystemArray, + wellKnownTypes.SystemCollectionsGenericIList_1.GenericInstantiate(this.ContainingSymbol, [this.ElementType]), + wellKnownTypes.SystemCollectionsGenericIReadOnlyList_1.GenericInstantiate(this.ContainingSymbol, [this.ElementType])]; + } + + private ImmutableArray BuildDefinedMembers() + { + if (this.Rank == 1) + { + // We need to re-add all members that should not be hidden by the base types + // IList will provide indexing + var iEnumerableBase = this.BaseTypes.First(b => b.Name == "IEnumerable" && b.IsGenericInstance); + var iListBase = this.BaseTypes.First(b => b.Name == "IList" && b.IsGenericInstance); + return iEnumerableBase.DefinedMembers + .Concat(iListBase.DefinedMembers) + .ToImmutableArray(); + } + else + { + // We just provide indexing + return [new ArrayIndexPropertySymbol(this)]; + } + } +} diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayLengthPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayLengthPropertySymbol.cs deleted file mode 100644 index 13d0d8f9c..000000000 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayLengthPropertySymbol.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Immutable; - -namespace Draco.Compiler.Internal.Symbols.Synthetized; - -/// -/// The length property of an array. -/// -internal sealed class ArrayLengthPropertySymbol : PropertySymbol -{ - public override string Name => "Length"; - - public override FunctionSymbol Getter { get; } - public override FunctionSymbol? Setter => null; - - public override TypeSymbol Type => this.ContainingSymbol.IndexType; - public override ArrayTypeSymbol ContainingSymbol { get; } - - public override bool IsIndexer => false; - public override bool IsStatic => false; - - public ArrayLengthPropertySymbol(ArrayTypeSymbol containingSymbol) - { - this.ContainingSymbol = containingSymbol; - this.Getter = new ArrayLengthGetSymbol(containingSymbol, this); - } -} - -/// -/// The getter of array Length. -/// -internal sealed class ArrayLengthGetSymbol( - ArrayTypeSymbol containingSymbol, - PropertySymbol propertySymbol) : FunctionSymbol, IPropertyAccessorSymbol -{ - public override ImmutableArray Parameters => []; - public override TypeSymbol ReturnType => this.ContainingSymbol.IndexType; - public override bool IsStatic => false; - - public override ArrayTypeSymbol ContainingSymbol { get; } = containingSymbol; - public PropertySymbol Property { get; } = propertySymbol; -} diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayTypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayTypeSymbol.cs deleted file mode 100644 index 7af265330..000000000 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayTypeSymbol.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; - -namespace Draco.Compiler.Internal.Symbols.Synthetized; - -/// -/// An array modeled as generics. -/// -internal sealed class ArrayTypeSymbol : TypeSymbol -{ - /// - /// The element type this array stores. - /// - public TypeParameterSymbol ElementType { get; } - - /// - /// The index type this array can be indexed with. - /// - public TypeSymbol IndexType { get; } - - /// - /// The rank of the array (number of dimensions). - /// - public int Rank { get; } - - public override string Name => this.Rank switch - { - 1 => "Array", - int n => $"Array{n}D", - }; - - public override ImmutableArray GenericParameters => [this.ElementType]; - - public override IEnumerable DefinedMembers => - [ - new ArrayLengthPropertySymbol(this) as Symbol, - new ArrayIndexPropertySymbol(this), - ]; - - public ArrayTypeSymbol(int rank, TypeSymbol indexType) - { - this.ElementType = new SynthetizedTypeParameterSymbol(this, "T"); - this.Rank = rank; - this.IndexType = indexType; - } - - public TypeSymbol GenericInstantiate(TypeSymbol elementType) => - this.GenericInstantiate(containingSymbol: null, ImmutableArray.Create(elementType)); - - public override string ToString() => $"{this.Name}{this.GenericsToString()}"; -} diff --git a/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs index 2183bcc47..279865af4 100644 --- a/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs @@ -31,6 +31,16 @@ internal abstract partial class TypeSymbol : Symbol, IMemberSymbol /// public virtual bool IsDelegateType => false; + /// + /// True, if this type is an enum type. + /// + public virtual bool IsEnumType => false; + + /// + /// True. if this is a native .NET array type. + /// + public virtual bool IsArrayType => false; + /// /// True, if this type is an interface. /// @@ -100,6 +110,7 @@ internal abstract partial class TypeSymbol : Symbol, IMemberSymbol public override TypeSymbol? GenericDefinition => null; public bool IsStatic => true; + public bool IsExplicitImplementation => false; /// /// The invokable function, if this is a delegate type. diff --git a/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.cs b/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.cs index fae67bfba..6eed87a1f 100644 --- a/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.cs +++ b/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.cs @@ -12,6 +12,7 @@ using Draco.Compiler.Internal.Symbols.Generic; using Draco.Compiler.Internal.Symbols.Metadata; using Draco.Compiler.Internal.Symbols.Synthetized; +using Draco.Compiler.Internal.Symbols.Synthetized.Array; using static Draco.Compiler.Internal.OptimizingIr.InstructionFactory; namespace Draco.Compiler.Internal.Symbols; @@ -64,7 +65,7 @@ private IEnumerable GenerateWellKnownTypes() for (var i = 2; i <= 8; ++i) { // Type - var arrayType = new ArrayTypeSymbol(i, this.SystemInt32); + var arrayType = new ArrayTypeSymbol(compilation, i, this.SystemInt32); yield return arrayType; // Ctor yield return new ArrayConstructorSymbol(arrayType); @@ -170,12 +171,28 @@ private static SynthetizedAliasSymbol Alias(string name, TypeSymbol type) => public TypeSymbol InstantiateArray(TypeSymbol elementType, int rank = 1) => rank switch { 1 => this.ArrayType.GenericInstantiate(elementType), - int n => new ArrayTypeSymbol(n, this.SystemInt32).GenericInstantiate(elementType), + int n => new ArrayTypeSymbol(compilation, n, this.SystemInt32).GenericInstantiate(elementType), }; #endregion - public ArrayTypeSymbol ArrayType => LazyInitializer.EnsureInitialized(ref this.array, () => new(1, this.SystemInt32)); + #region Additives/Mixins for types + /// + /// Returns all the equality operator members for an enum type. + /// + /// The enum type. + /// The equality operator members. + public IEnumerable GetEnumEqualityMembers(TypeSymbol type) + { + if (!type.IsEnumType) throw new ArgumentException("the type must be an enum type", nameof(type)); + + // == and != + yield return this.Comparison(TokenKind.Equal, type, type, FromAllocating(this.CodegenEqual)); + yield return this.Comparison(TokenKind.NotEqual, type, type, FromAllocating(this.CodegenNotEqual)); + } + #endregion + + public ArrayTypeSymbol ArrayType => LazyInitializer.EnsureInitialized(ref this.array, () => new(compilation, 1, this.SystemInt32)); private ArrayTypeSymbol? array; public ArrayConstructorSymbol ArrayCtor => LazyInitializer.EnsureInitialized(ref this.arrayCtor, () => new(this.ArrayType)); diff --git a/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.xml b/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.xml index 5a17daf47..f554953e9 100644 --- a/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.xml +++ b/src/Draco.Compiler/Internal/Symbols/WellKnownTypes.xml @@ -29,7 +29,11 @@ + + + + diff --git a/src/Draco.LanguageServer/DracoLanguageServer.cs b/src/Draco.LanguageServer/DracoLanguageServer.cs index 5fc45dd38..03184ecd3 100644 --- a/src/Draco.LanguageServer/DracoLanguageServer.cs +++ b/src/Draco.LanguageServer/DracoLanguageServer.cs @@ -93,7 +93,7 @@ private async Task CreateCompilation() syntaxTrees: syntaxTrees, metadataReferences: designTimeBuild.References - .Select(r => MetadataReference.FromPeStream(r.OpenRead())) + .Select(r => MetadataReference.FromFile(r.FullName)) .ToImmutableArray(), rootModulePath: rootPath); } diff --git a/src/Draco.ProjectSystem/Project.cs b/src/Draco.ProjectSystem/Project.cs index 2231511a2..766bc1a6f 100644 --- a/src/Draco.ProjectSystem/Project.cs +++ b/src/Draco.ProjectSystem/Project.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using Microsoft.Build.Evaluation; -using Microsoft.Build.Framework; using MSBuildProject = Microsoft.Build.Evaluation.Project; using MSBuildProjectInstance = Microsoft.Build.Execution.ProjectInstance; diff --git a/src/Draco.Repl/ReplPromptCallbacks.cs b/src/Draco.Repl/ReplPromptCallbacks.cs index 4b331ddc3..edc5135e9 100644 --- a/src/Draco.Repl/ReplPromptCallbacks.cs +++ b/src/Draco.Repl/ReplPromptCallbacks.cs @@ -7,7 +7,6 @@ using Draco.Compiler.Api.Scripting; using Draco.Compiler.Api.Syntax; using PrettyPrompt; -using PrettyPrompt.Completion; using PrettyPrompt.Consoles; using PrettyPrompt.Documents; using PrettyPrompt.Highlighting; diff --git a/src/Draco.SourceGeneration/Templates/WellKnownTypes.sbncs b/src/Draco.SourceGeneration/Templates/WellKnownTypes.sbncs index 2ba6ffccb..dc1bcd10d 100644 --- a/src/Draco.SourceGeneration/Templates/WellKnownTypes.sbncs +++ b/src/Draco.SourceGeneration/Templates/WellKnownTypes.sbncs @@ -6,7 +6,9 @@ using Draco.Compiler.Internal.Symbols.Synthetized; {{include 'Utils.sbncs'}} {{func nameify(name)}} - {{ret string.replace(name, '.', '')}} + {{$result = string.replace(name, '.', '')}} + {{$result = string.replace($result, '`', '_')}} + {{ret $result}} {{end}} #nullable enable