diff --git a/Samples/NestedType/.vscode/launch.json b/Samples/NestedType/.vscode/launch.json new file mode 100644 index 0000000..078d2e2 --- /dev/null +++ b/Samples/NestedType/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/struct.dll", + "args": [], + "cwd": "${workspaceFolder}/bin/Debug/", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "integratedTerminal", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/Samples/NestedType/.vscode/tasks.json b/Samples/NestedType/.vscode/tasks.json new file mode 100644 index 0000000..31c32bd --- /dev/null +++ b/Samples/NestedType/.vscode/tasks.json @@ -0,0 +1,24 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "shell", + "args": [ + "build", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Samples/NestedType/NestedType.out b/Samples/NestedType/NestedType.out new file mode 100644 index 0000000..f2b6d30 --- /dev/null +++ b/Samples/NestedType/NestedType.out @@ -0,0 +1,4 @@ +10 +10 +100 +100 diff --git a/Samples/NestedType/NestedType.pys b/Samples/NestedType/NestedType.pys new file mode 100644 index 0000000..02e3eff --- /dev/null +++ b/Samples/NestedType/NestedType.pys @@ -0,0 +1,29 @@ +struct Rect +{ + var p1: Vector2Int + var p2: Vector2Int + + struct Vector2Int + { + var x : int + var y : int + } +} + + +function main() +{ + var rect = Rect() + //rect.p1 = Rect.Vector2Int() + rect.p1.x = 10 + /* + rect.p1.y = 10 + rect.p2.x = 100 + rect.p2.y = 100 + + print(rect.p1.x) + print(rect.p1.y) + print(rect.p2.x) + print(rect.p2.y) + //*/ +} \ No newline at end of file diff --git a/Samples/NestedType/NestedType.pysproj b/Samples/NestedType/NestedType.pysproj new file mode 100644 index 0000000..efa7319 --- /dev/null +++ b/Samples/NestedType/NestedType.pysproj @@ -0,0 +1,6 @@ + + + Exe + netcoreapp3.1 + + \ No newline at end of file diff --git a/src/Compiler.Tests.Samples/ConsoleOutputTests.cs b/src/Compiler.Tests.Samples/ConsoleOutputTests.cs index 23d7cc9..50e9ce0 100644 --- a/src/Compiler.Tests.Samples/ConsoleOutputTests.cs +++ b/src/Compiler.Tests.Samples/ConsoleOutputTests.cs @@ -27,6 +27,7 @@ public ConsoleOutputTests(ITestOutputHelper output) [InlineData("IsEven", "2", "10", "5")] [InlineData("Enum")] [InlineData("Struct")] + //[InlineData("NestedType")] public async Task SamplesTests(string filenamePrefix, params string[] inputs) { var psi = new ProcessStartInfo diff --git a/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs b/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs index e5a59d3..1d07e27 100644 --- a/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs +++ b/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Compiler.CodeAnalysis; using Compiler.CodeAnalysis.Diagnostics; using Compiler.CodeAnalysis.Symbols; @@ -606,6 +605,30 @@ function main() }; AssertDiagnostics(text, diagnostics); } + + [Fact] + public void Binder_MemberAccess_Reports_UnexpectedToken() + { + var text = @" + struct TestStruct + { + } + + function main() + { + TestStruct. [=] 10 + TestStruct.[(][)] + } + "; + + var diagnostics = new List() + { + DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.EqualsToken, SyntaxKind.IdentifierToken), + DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.OpenParenthesisToken, SyntaxKind.IdentifierToken), + DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.CloseParenthesisToken, SyntaxKind.IdentifierToken) + }; + AssertDiagnostics(text, diagnostics); + } [Fact] public void Binder_NameExpression_Reports_NoErrorForInsertedToken() @@ -703,7 +726,7 @@ public void Binder_Parameter_Already_Declared() var text = @" function sum(a: int, b: int, [a: int]) : int { - return a + b + c + return a + b } "; @@ -919,20 +942,20 @@ public void Binder_MultiLineComment_ReportsUnterminated() } [Fact] - public void Binder_Cannot_Access_Member() + public void Binder_MemberAccess_Reports_CannotAccessMember() { const string? text = @" function main() { var p: int = 0 p.[length] = 10 - p.[length()] + p.[length]() } "; var diagnostics = new List() { DiagnosticCode.CannotAccessMember.GetDiagnostic("length", "int"), - DiagnosticCode.CannotAccessMember.GetDiagnostic("length", "int"), + DiagnosticCode.UndefinedFunction.GetDiagnostic("length"), }; AssertDiagnostics(text, diagnostics); } @@ -1095,6 +1118,33 @@ function main() AssertDiagnostics(text, diagnostics); } + [Fact] + public void Binder_MemberAccess_Nested_Reports_CannotAccessMember() + { + var text = @" + struct Point + { + } + + struct Line + { + var start: Point + } + + function main() + { + var nested = Line() + var x = nested.start.[x] + } + "; + + var diagnostics = new List() + { + DiagnosticCode.CannotAccessMember.GetDiagnostic("x", "Point"), + }; + AssertDiagnostics(text, diagnostics); + } + [Fact] public void Binder_MemberAccess_NestedCall() { @@ -1239,8 +1289,12 @@ struct TestStruct function f(i : int) { print(b) - print(self.a + 1) - print(i) + g() + } + + function g() + { + } } "; @@ -1276,7 +1330,7 @@ function f() AssertDiagnostics(text, diagnostics); } - + [Fact] public void Binder_SelfExpression_Reports_CannotUseSelfOutsideOfReceiverFunctions() { @@ -1365,6 +1419,90 @@ enum TestEnum AssertDiagnostics(text, diagnostics); } + [Fact] + public void Binder_TypeDeclaration_NestedType() + { + var text = @" + struct Line + { + var p1 : Point + var p2 : Point + + struct Point + { + var x : int + var y : int + } + }"; + + var diagnostics = new List() + { + }; + + AssertDiagnostics(text, diagnostics); + } + + [Fact] + public void Binder_VariableDeclaration_NestedType() + { + var text = @" + struct Line + { + var p1 : Point + var p2 : Point + + struct Point + { + var x : int + var y : int + + struct Point1 + { + } + } + } + + function main() + { + var p = Line.Point.Point1() + }"; + + var diagnostics = new List() + { + }; + + AssertDiagnostics(text, diagnostics); + } + + [Fact] + public void Binder_MemberAccess_NestedType() + { + var text = @" + struct Line + { + var p1 : Point + var p2 : Point + + struct Point + { + var x : int + var y : int + } + } + + function main() + { + var line = Line() + line.p1.x = 10 + }"; + + var diagnostics = new List() + { + }; + + AssertDiagnostics(text, diagnostics); + } + /* [Fact] public void Binder_IfStatement_Reports_UnreachableCode_Warning() @@ -1475,6 +1613,32 @@ function a(x : string) } } + "; + + var diagnostics = new List() + { + }; + + AssertDiagnostics(text, diagnostics); + } + + + [Fact] + public void Binder_MemberAccess_SupportsOverloading() + { + var text = @" + struct TestStruct + { + function a(x : int) + { + a(string(x)) + } + + function a(x : string) + { + + } + } function main() { diff --git a/src/Compiler/CodeAnalysis/Binding/Binder.cs b/src/Compiler/CodeAnalysis/Binding/Binder.cs index dbebd67..4552cda 100644 --- a/src/Compiler/CodeAnalysis/Binding/Binder.cs +++ b/src/Compiler/CodeAnalysis/Binding/Binder.cs @@ -39,18 +39,8 @@ public static BoundGlobalScope BindGlobalScope(ImmutableArray syntax { var parentScope = CreateRootScope(); var binder = new Binder(parentScope, null, null); - binder.Diagnostics.AddRange(syntaxTrees.SelectMany(st => st.Diagnostics)); - if (binder.Diagnostics.HasErrors()) - { - return new BoundGlobalScope( - binder.Diagnostics.ToImmutableArray(), - null, - ImmutableArray.Empty, - ImmutableArray.Empty); - } - - + var typeDeclarations = syntaxTrees.SelectMany(st => st.Root.Members) .OfType(); @@ -99,17 +89,24 @@ private void BindTypeDeclaration(TypeDeclarationSyntax typeDeclarationSyntax) } _scope.TryDeclareType(type); + + _scope = type.BoundScope!; + + // Declare all nested types + foreach (var statementSyntax in typeDeclarationSyntax.Body.Statement.OfType()) + { + BindTypeDeclaration(statementSyntax); + } + + _scope = type.BoundScope!.Parent!; } public static BoundProgram BindProgram(BoundGlobalScope globalScope) { - if (globalScope.Diagnostics.HasErrors()) - { - return EmptyProgram(globalScope); - } + var diagnostics = ImmutableArray.CreateBuilder(); + diagnostics.AddRange(globalScope.Diagnostics); var functionBodies = ImmutableDictionary.CreateBuilder(); - var diagnostics = ImmutableArray.CreateBuilder(); var functionsToLower = new List(globalScope.Functions.Where(f => f.Declaration != null)); @@ -133,7 +130,7 @@ public static BoundProgram BindProgram(BoundGlobalScope globalScope) } Debug.Assert(function.BoundScope != null); - var binder = new Binder(function.BoundScope, function, function.Receiver); + var binder = new Binder(function.BoundScope, function, function.ReceiverType); var body = binder.BindStatement(function.Declaration!.Body); var loweredBody = Lowerer.Lower(function, body); @@ -152,14 +149,6 @@ public static BoundProgram BindProgram(BoundGlobalScope globalScope) globalScope.Types); } - private static BoundProgram EmptyProgram(BoundGlobalScope globalScope) - { - return new BoundProgram(globalScope.Diagnostics, - null, - ImmutableDictionary.Empty, - ImmutableArray.Empty); - } - private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax) { var parameters = ImmutableArray.CreateBuilder(); @@ -175,7 +164,7 @@ private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax) } else { - var parameter = VariableSymbol.Parameter(parameterSyntax, parameterName, false, parameterType); + var parameter = VariableSymbol.Parameter(parameterSyntax, parameterName, false, parameterType); parameters.Add(parameter); } } @@ -188,7 +177,7 @@ private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax) syntax, _scope, receiver); - + _scope.TryDeclareFunction(function); return function; } @@ -207,19 +196,28 @@ private StructSymbol BindStructDeclaration(StructDeclarationSyntax syntax) private void BindMemberBlockStatement(MemberBlockStatementSyntax syntax, List functionsToLower) { - if (_scope is not TypeBoundScope typeScope) + var typeScope = _scope as TypeBoundScope; + Debug.Assert(typeScope != null); + + var type = typeScope.OwnerType; + foreach (var statementSyntax in syntax.Statement.OfType()) { - throw new InvalidOperationException(); + var nestedType = typeScope.TryLookupSymbol(statementSyntax.Identifier.Text); + Debug.Assert(nestedType != null); + Debug.Assert(nestedType.BoundScope != null); + + var binder = new Binder(nestedType.BoundScope, null, nestedType); + binder.BindMemberBlockStatement(nestedType.Declaration!.Body, functionsToLower); + Diagnostics.AddRange(binder.Diagnostics); } - var type = typeScope.OwnerType; foreach (var statementSyntax in syntax.Statement) { switch (statementSyntax.Kind) { case SyntaxKind.VariableDeclarationStatement: var variableStatement = BindVariableDeclarationStatement((VariableDeclarationStatementSyntax)statementSyntax, false); - var field = new FieldSymbol((BoundVariableDeclarationStatement)variableStatement); + var field = new FieldSymbol((BoundVariableDeclarationStatement)variableStatement, type); typeScope.TryDeclareField(field); break; @@ -249,7 +247,11 @@ private void BindMemberBlockStatement(MemberBlockStatementSyntax syntax, List(".ctor"); } if (symbol is not FunctionSymbol function) @@ -848,73 +840,55 @@ private BoundExpression BindCallExpression(CallExpressionSyntax syntax, Function private BoundExpression BindMemberAccessExpression(MemberAccessExpressionSyntax syntax) { - BoundExpression nameExpression; - if (syntax.ParentExpression.Kind == SyntaxKind.MemberAccessExpression) - { - return BindMemberAccessExpression((MemberAccessExpressionSyntax)syntax.ParentExpression); - } - else + var originalScope = _scope; + try { - nameExpression = BindNameExpression((NameExpressionSyntax)syntax.ParentExpression, true); - switch (syntax.MemberExpression.Kind) - { - case SyntaxKind.CallExpression: - var functionSymbol = GetMemberSymbol(nameExpression.Type, syntax.IdentifierToken.Text); - if (functionSymbol != null) - { - var boundCall = (BoundCallExpression)BindCallExpression((CallExpressionSyntax)syntax.MemberExpression, functionSymbol); - return new BoundMemberAccessExpression(syntax, nameExpression, boundCall); - } - break; + var members = new Stack(); + var parent = syntax.ParentExpression; - default: - var member = BindMemberReference(nameExpression.Type, syntax); - if (member != null) - { - return new BoundMemberAccessExpression(syntax, nameExpression, member); - } - break; + members.Push(syntax.MemberExpression); + + while (parent is MemberAccessExpressionSyntax access) + { + members.Push(access.MemberExpression); + parent = access.ParentExpression; } - } - Diagnostics.ReportCannotAccessMember(syntax.MemberExpression.Location, nameExpression.Type.Name, syntax.MemberExpression.IdentifierToken.Text); - return new BoundErrorExpression(syntax); - } + BoundExpression boundParent = BindNameExpression(parent, true); + BoundExpression receiverExpression; + _scope = boundParent.Type.BoundScope!; - private static BoundMemberExpression? BindMemberReference(TypeSymbol typeSymbol, MemberAccessExpressionSyntax syntax) - { - var memberSymbol = GetMemberSymbol(typeSymbol, syntax.IdentifierToken.Text); - if (memberSymbol != null) - { - return new BoundMemberExpression(syntax, memberSymbol); - } - return null; - } - - private static T? GetMemberSymbol(TypeSymbol typeSymbol, string memberName) where T : MemberSymbol - { - foreach (var member in typeSymbol.Members.OfType()) - { - if (member.Name == memberName) + do { - return member; - } - } - return null; - } + // Advance the scope trough the nested access + var exp = members.Pop(); + var boundMember = BindMemberOrNestedTypeExpression(exp); - private static MemberSymbol? GetMemberSymbol(TypeSymbol typeSymbol, string memberName) - { - foreach (var member in typeSymbol.Members) + Debug.Assert(boundMember.Type.BoundScope != null); + _scope = boundMember.Type.BoundScope; + + if (boundMember.Kind == BoundNodeKind.ErrorExpression) + { + return boundMember; + } + else if (boundMember.Kind == BoundNodeKind.TypeReferenceExpression) + { + receiverExpression = new BoundNestedTypeAccessExpression(syntax, boundParent, (BoundTypeReferenceExpression)boundMember); + } + else + { + receiverExpression = new BoundMemberAccessExpression(syntax, boundParent, (BoundMemberExpression)boundMember); + } + boundParent = receiverExpression; + } while (members.Count > 0); + return receiverExpression; + } + finally { - if (member.Name == memberName) - { - return member; - } + _scope = originalScope; } - return null; } - + private BoundExpression BindConversion(ExpressionSyntax syntax, TypeSymbol type, bool allowExplicit = false) { var expression = BindExpression(syntax); @@ -947,6 +921,44 @@ private BoundExpression BindConversion(TextLocation diagnosticLocation, BoundExp return new BoundConversionExpression(expression.Syntax, type, expression); } + private BoundExpression BindMemberOrNestedTypeExpression(NameExpressionSyntax syntax) + { + if (syntax.IdentifierToken.IsMissing) + { + return new BoundErrorExpression(syntax); + } + + if (syntax.Kind == SyntaxKind.CallExpression) + { + return BindCallExpression((CallExpressionSyntax)syntax); + } + + var typeScope = _scope as TypeBoundScope; + Debug.Assert(typeScope != null); + + var symbol = BindSymbolReference(syntax.IdentifierToken, false); + if (symbol == null) + { + Diagnostics.ReportCannotAccessMember(syntax.Location, typeScope.OwnerType.Name, syntax.IdentifierToken.Text); + return new BoundErrorExpression(syntax); + } + + switch (symbol.Kind) + { + case SymbolKind.Member: + case SymbolKind.Function: // TODO - HACK: Functions should be members + return new BoundMemberExpression(syntax, (MemberSymbol)symbol); + + case SymbolKind.Type: + case SymbolKind.Enum: + case SymbolKind.Struct: + return new BoundTypeReferenceExpression(syntax, (TypeSymbol)symbol); + + default: + throw new InvalidOperationException($"Unexpected symbol of kind {symbol.Kind}"); + } + } + private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byReference = false) { if (syntax.IdentifierToken.IsMissing) @@ -961,7 +973,7 @@ private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byR return BindSelfKeyword((SelfKeywordSyntax)syntax); } - var symbol = BindSymbolReference(syntax.IdentifierToken, syntax.IdentifierToken.Location); + var symbol = BindSymbolReference(syntax.IdentifierToken); if (symbol == null) { // No need to report an error @@ -974,13 +986,17 @@ private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byR case SymbolKind.Variable: return new BoundVariableExpression(syntax, (VariableSymbol)symbol, byReference); - case SymbolKind.Member: - var selfKeyword = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.SelfKeyword, syntax.Span.End, "self", null, ImmutableArray.Empty, ImmutableArray.Empty); - var injectedSelf = new SelfKeywordSyntax(syntax.SyntaxTree, selfKeyword); - var dotToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.DotToken, syntax.Span.End, ".", null, ImmutableArray.Empty, ImmutableArray.Empty); - var memberAccess = new MemberAccessExpressionSyntax(syntax.SyntaxTree, injectedSelf, dotToken, syntax); - return BindMemberAccessExpression(memberAccess); + // If we get here, means an access to a member + // without the use of the SelfKeyword. + // So we synthesize a syntax with the self, + // and pass to the apropriate method + var keywordToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.SelfKeyword, -1, SyntaxKind.SelfKeyword.GetText(), null, ImmutableArray.Empty, ImmutableArray.Empty); + var selfExpression = new SelfKeywordSyntax(syntax.SyntaxTree, keywordToken); + var dotToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.DotToken, -1, SyntaxKind.DotToken.GetText(), null, ImmutableArray.Empty, ImmutableArray.Empty); + var accessSyntax = new MemberAccessExpressionSyntax(syntax.SyntaxTree, + selfExpression, dotToken, syntax); + return BindMemberAccessExpression(accessSyntax); case SymbolKind.Type: case SymbolKind.Enum: @@ -1001,13 +1017,13 @@ private BoundExpression BindSelfKeyword(SelfKeywordSyntax syntax) return new BoundErrorExpression(syntax); } - if (_function.Receiver == null) + if (_function.ReceiverType == null) { Diagnostics.ReportCannotUseSelfOutsideOfReceiverFunctions(syntax.IdentifierToken.Location, _function.Name); return new BoundErrorExpression(syntax); } - return new BoundSelfExpression(syntax, _function.Receiver); + return new BoundSelfExpression(syntax, _function.ReceiverType); } private TypeSymbol? LookupType(string name) diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs b/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs new file mode 100644 index 0000000..3f6935e --- /dev/null +++ b/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs @@ -0,0 +1,23 @@ +using Compiler.CodeAnalysis.Symbols; +using Compiler.CodeAnalysis.Syntax; + +namespace Compiler.CodeAnalysis.Binding +{ + internal class BoundNestedTypeAccessExpression : BoundExpression + { + public BoundExpression Instance { get; } + public BoundTypeReferenceExpression Member { get; } + public override TypeSymbol Type => Member.Type; + public override BoundNodeKind Kind => BoundNodeKind.NestedTypeAccessExpression; + + + public BoundNestedTypeAccessExpression(SyntaxNode syntax, + BoundExpression instance, + BoundTypeReferenceExpression member) + : base(syntax) + { + Instance = instance; + Member = member; + } + } +} \ No newline at end of file diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs b/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs index cf6b2b1..7d08d69 100644 --- a/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs +++ b/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs @@ -31,5 +31,6 @@ public enum BoundNodeKind MemberAccessExpression, SelfExpression, MemberExpression, + NestedTypeAccessExpression, } } \ No newline at end of file diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs b/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs index af537bc..9653a9b 100644 --- a/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs +++ b/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs @@ -104,11 +104,20 @@ public static void WriteTo(this BoundNode node, IndentedTextWriter writer) case BoundNodeKind.SelfExpression: WriteSelfExpression(writer); break; + case BoundNodeKind.NestedTypeAccessExpression: + WriteNestedTypeAccessExpression((BoundNestedTypeAccessExpression)node, writer); + break; default: throw new InvalidOperationException($"Unexpected node {node.Kind}"); } } + private static void WriteNestedTypeAccessExpression(BoundNestedTypeAccessExpression node, IndentedTextWriter writer) + { + node.Instance.WriteTo(writer); + node.Type.WriteTo(writer); + } + private static void WriteMemberExpression(BoundMemberExpression node, IndentedTextWriter writer) { node.Symbol.WriteTo(writer); diff --git a/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs b/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs index 035f5af..643302d 100644 --- a/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs +++ b/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs @@ -217,6 +217,8 @@ public BoundExpression RewriteExpression(BoundExpression expression) return RewriteTypeReferenceExpression((BoundTypeReferenceExpression)expression); case BoundNodeKind.MemberExpression: return RewriteMemberExpression((BoundMemberExpression)expression); + case BoundNodeKind.NestedTypeAccessExpression: + return RewriteNestedTypeAccessExpression((BoundNestedTypeAccessExpression)expression); default: throw new InvalidOperationException($"Unexpected expression {expression.Kind}."); } @@ -324,5 +326,10 @@ protected virtual BoundExpression RewriteMemberExpression(BoundMemberExpression { return node; } + + protected virtual BoundExpression RewriteNestedTypeAccessExpression(BoundNestedTypeAccessExpression node) + { + return node; + } } } \ No newline at end of file diff --git a/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs b/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs index 00c0beb..71da5bf 100644 --- a/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs +++ b/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs @@ -22,7 +22,6 @@ public enum DiagnosticCode UndefinedType, CannotConvertImplicitly, ParameterAlreadyDeclared, - FunctionsAreUnsupported, InvalidBreakOrContinue, InvalidReturnExpression, MissingReturnExpression, @@ -70,7 +69,6 @@ public static class DiagnosticCodeExtension {DiagnosticCode.UndefinedType, "Type '{0}' doesn't exist."}, {DiagnosticCode.CannotConvertImplicitly, "Cannot convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?)"}, {DiagnosticCode.ParameterAlreadyDeclared, "A parameter with the name '{0}' already exists."}, - {DiagnosticCode.FunctionsAreUnsupported, "Functions with return values are unsupported."}, {DiagnosticCode.InvalidBreakOrContinue, "The keyword '{0}' can only be used inside of loops."}, {DiagnosticCode.InvalidReturnExpression, "Since the function '{0}' does not return a value the 'return' keyword cannot be followed by an expression."}, {DiagnosticCode.MissingReturnExpression, "An expression of type '{0}' is expected."}, diff --git a/src/Compiler/CodeAnalysis/Emit/Emitter.cs b/src/Compiler/CodeAnalysis/Emit/Emitter.cs index ce19f6a..ec4a746 100644 --- a/src/Compiler/CodeAnalysis/Emit/Emitter.cs +++ b/src/Compiler/CodeAnalysis/Emit/Emitter.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using Compiler.CodeAnalysis.Binding; +using Compiler.CodeAnalysis.Binding.Scopes; using Compiler.CodeAnalysis.Diagnostics; using Compiler.CodeAnalysis.Symbols; using Compiler.CodeAnalysis.Syntax; @@ -12,11 +13,23 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using Mono.Collections.Generic; namespace Compiler.CodeAnalysis.Emit { internal class Emitter { + const TypeAttributes _enumAttributes = TypeAttributes.Class + | TypeAttributes.NotPublic + | TypeAttributes.AnsiClass + | TypeAttributes.Sealed; + + const TypeAttributes _structAttributes = TypeAttributes.Class + //| TypeAttributes.Public + | TypeAttributes.SequentialLayout + | TypeAttributes.AnsiClass + | TypeAttributes.Sealed + | TypeAttributes.BeforeFieldInit; private readonly DiagnosticBag _diagnostics; private readonly AssemblyDefinition _assemblyDefinition; private readonly TypeDefinition _typeDefinition; @@ -260,6 +273,8 @@ private void EmitTypeDeclarations(ImmutableArray types) private void EmitTypeBody(TypeSymbol typeSymbol) { + Debug.Assert(typeSymbol.BoundScope != null); + var typeDefinition = _declaredTypes[typeSymbol]; EmitFields(typeSymbol, typeDefinition); @@ -275,6 +290,13 @@ private void EmitTypeBody(TypeSymbol typeSymbol) default: throw new InvalidOperationException($"Unexpected declaration kind {typeSymbol.Declaration.TypeKind}"); } + + + var nestedTypes = typeSymbol.BoundScope.GetDeclaredSymbols(); + foreach (var nestedType in nestedTypes) + { + EmitTypeBody(nestedType); + } } private void EmitFields(TypeSymbol typeSymbol, TypeDefinition typeDefinition) @@ -308,19 +330,33 @@ private void EmitFields(TypeSymbol typeSymbol, TypeDefinition typeDefinition) private void EmitTypeDeclaration(TypeSymbol typeSymbol) { + Debug.Assert(typeSymbol.BoundScope != null); + + TypeAttributes? modifiers = null; + if (typeSymbol.BoundScope.Parent is TypeBoundScope) + { + modifiers = TypeAttributes.NestedPublic; + } + switch (typeSymbol.Declaration!.TypeKind) { case TypeDeclarationKind.Enum: - EmitEnumDeclaration((EnumSymbol)typeSymbol); + EmitEnumDeclaration((EnumSymbol)typeSymbol, modifiers); break; case TypeDeclarationKind.Struct: - EmitStructDeclaration((StructSymbol)typeSymbol); + EmitStructDeclaration((StructSymbol)typeSymbol, modifiers); break; default: throw new InvalidOperationException($"Unexpected declaration kind {typeSymbol.Declaration.TypeKind}"); } + + var nestedTypes = typeSymbol.BoundScope.GetDeclaredSymbols(); + foreach (var nestedType in nestedTypes) + { + EmitTypeDeclaration(nestedType); + } } private void EmitFunctionDeclarations(ImmutableDictionary functions) @@ -339,7 +375,7 @@ private void EmitFunctionDeclarations(ImmutableDictionary GetCollectionFor(TypeSymbol type) + { + Debug.Assert(type.BoundScope != null); + if (type.BoundScope.Parent is not TypeBoundScope parentScope) + { + return _assemblyDefinition.MainModule.Types; + } + if (type.BoundScope.Parent == null) + { + return _assemblyDefinition.MainModule.Types; + } + var parentType = parentScope.OwnerType; + var parentTypeDefinition = _declaredTypes[parentType]; + return parentTypeDefinition.NestedTypes; + } - private void EmitStructDeclaration(StructSymbol structSymbol) + private void EmitStructDeclaration(StructSymbol structSymbol, TypeAttributes? modifiers) { - const TypeAttributes _structAttributes = TypeAttributes.Class - | TypeAttributes.Public - | TypeAttributes.SequentialLayout - | TypeAttributes.AnsiClass - | TypeAttributes.Sealed - | TypeAttributes.BeforeFieldInit; - - var structType = new TypeDefinition("", structSymbol.Name, _structAttributes, Import(TypeSymbol.Struct)); - _assemblyDefinition.MainModule.Types.Add(structType); + var attributes = _structAttributes; + if (modifiers.HasValue) + { + attributes |= modifiers.Value; + } + var structType = new TypeDefinition("", structSymbol.Name, attributes, Import(TypeSymbol.Struct)); + GetCollectionFor(structSymbol).Add(structType); _declaredTypes.Add(structSymbol, structType); _resolvedTypes.Add(structSymbol, structType); - + // Forward-declare empty constructor var emptyCtorDefinition = new MethodDefinition( ".ctor", @@ -459,14 +511,12 @@ private void EmitEmptyConstructorForStruct(StructSymbol structSymbol, TypeDefini { // Get empty constructor declaration var constructor = typeDefinition.Methods[0]; - var ilProcessor = constructor.Body.GetILProcessor(); foreach (var field in structSymbol.Members.OfType()) { var fieldDefinition = typeDefinition.Fields.Single(f => f.Name == field.Name); - var defaultValue = new BoundLiteralExpression(null!, field.Type, field.Type.DefaultValue!); - EmitFieldAssignment(ilProcessor, defaultValue, fieldDefinition); + EmitFieldAssignment(ilProcessor, field.Initializer, fieldDefinition); } ilProcessor.Emit(OpCodes.Ret); @@ -661,11 +711,37 @@ private void EmitExpression(ILProcessor ilProcessor, BoundExpression node) EmitSelfExpression(ilProcessor); break; + case BoundNodeKind.MemberExpression: + EmitMemberExpression(ilProcessor, (BoundMemberExpression)node); + break; + default: throw new InvalidOperationException($"Unexpected node kind {node.Kind}"); } } + private void EmitMemberExpression(ILProcessor ilProcessor, BoundMemberExpression node) + { + Console.WriteLine($"EmitMemberExpression: {node}"); + Debug.Assert(node.Symbol.ReceiverType != null); + var typeDefinition = Import(node.Symbol.ReceiverType).Resolve(); + Debug.Assert(typeDefinition != null); + + switch (node.MemberKind) + { + case MemberKind.Field: + EmitFieldAccessExpression(ilProcessor, (FieldSymbol)node.Symbol, typeDefinition); + break; + + case MemberKind.Method: + EmitCallExpression(ilProcessor, (BoundCallExpression)node, typeDefinition); + break; + + default: + throw new InvalidOperationException($"Unexpected member type {node.MemberKind}"); + } + } + private static void EmitTypeReferenceExpression(ILProcessor ilProcessor, BoundTypeReferenceExpression node) { // HACK - This is not the rigth way to handle statics @@ -674,27 +750,31 @@ private static void EmitTypeReferenceExpression(ILProcessor ilProcessor, BoundTy private static void EmitConstantExpression(ILProcessor ilProcessor, BoundExpression node) { - Debug.Assert(node.ConstantValue != null); + EmitConstantExpression(ilProcessor, node.Type, node.ConstantValue); + } - if (node.Type == TypeSymbol.Bool) + private static void EmitConstantExpression(ILProcessor ilProcessor, TypeSymbol type, BoundConstant? constant) + { + Debug.Assert(constant != null); + if (type == TypeSymbol.Bool) { - var value = (bool)node.ConstantValue.Value; + var value = (bool)constant.Value; var instruction = value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; ilProcessor.Emit(instruction); } - else if (node.Type == TypeSymbol.Int) + else if (type == TypeSymbol.Int) { - var value = (int)node.ConstantValue.Value; + var value = (int)constant.Value; ilProcessor.Emit(OpCodes.Ldc_I4, value); } - else if (node.Type == TypeSymbol.String) + else if (type == TypeSymbol.String) { - var value = (string)node.ConstantValue.Value; + var value = (string)constant.Value; ilProcessor.Emit(OpCodes.Ldstr, value); } else { - throw new InvalidOperationException($"Unexpected constant expression type: {node.Type}"); + throw new InvalidOperationException($"Unexpected constant expression type: {type}"); } } @@ -933,7 +1013,7 @@ private void EmitBinaryExpression(ILProcessor ilProcessor, BoundBinaryExpression } } - private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression node) + private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression node, TypeDefinition? typeDefinition = null) { var function = (FunctionSymbol)node.Symbol; EmitExpressions(ilProcessor, node.Arguments); @@ -948,12 +1028,13 @@ private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression nod } else if (node.Symbol.Name.Equals(".ctor")) { - var className = function.ReturnType.Name; - Console.WriteLine(className); - var structSymbol = _declaredTypes.First(s => s.Key.Name == className).Value; - + if (typeDefinition == null) + { + typeDefinition = Import(node.Symbol.Type).Resolve(); + } + Debug.Assert(typeDefinition != null); // TODO: We should use a general overload resolution algorithm instead - ilProcessor.Emit(OpCodes.Newobj, structSymbol.Methods[0]); + ilProcessor.Emit(OpCodes.Newobj, typeDefinition.Methods[0]); } else { @@ -1017,46 +1098,28 @@ private void EmitConversionExpression(ILProcessor ilProcessor, BoundConversionEx private void EmitMemberAccessExpression(ILProcessor ilProcessor, BoundMemberAccessExpression node) { - var typeDefinition = Import(node.Instance.Type).Resolve(); - Debug.Assert(typeDefinition != null); - - if (node.Instance.Kind == BoundNodeKind.SelfExpression) + if (node.Type.IsEnum()) { - EmitSelfExpression(ilProcessor); - } - else - { - EmitExpression(ilProcessor, node.Instance); + var field = (FieldSymbol)node.Member.Symbol; + EmitConstantExpression(ilProcessor, TypeSymbol.Int, field.Constant); + return; } - switch (node.Member.MemberKind) - { - case MemberKind.Field: - EmitFieldAccessExpression(ilProcessor, (FieldSymbol)node.Member.Symbol, typeDefinition); - break; - - case MemberKind.Method: - EmitCallExpression(ilProcessor, (BoundCallExpression)node.Member); - break; - - default: - throw new InvalidOperationException($"Unexpected member type {node.Member.MemberKind}"); - } + Console.WriteLine("EmitMemberAccessExpression"); + EmitExpression(ilProcessor, node.Instance); + EmitMemberExpression(ilProcessor, node.Member); } private static void EmitFieldAccessExpression(ILProcessor ilProcessor, FieldSymbol field, TypeDefinition typeDefinition) { - var fieldDefinition = GetField(field, typeDefinition); - Debug.Assert(fieldDefinition != null); - - if (fieldDefinition.Constant != null) + if (field.Constant != null) { - ilProcessor.Emit(OpCodes.Ldc_I4, (int)fieldDefinition.Constant); - } - else - { - ilProcessor.Emit(OpCodes.Ldfld, fieldDefinition); + EmitConstantExpression(ilProcessor, field.Type, field.Constant); } + + var fieldDefinition = GetField(field, typeDefinition); + Debug.Assert(fieldDefinition != null); + ilProcessor.Emit(OpCodes.Ldfld, fieldDefinition); } private static FieldDefinition? GetField(MemberSymbol member, TypeDefinition typeDefinition) @@ -1081,7 +1144,6 @@ private static void EmitFieldAccessExpression(ILProcessor ilProcessor, FieldSymb for (var i = 0; i < method.Parameters.Count; i++) { var other = method.Parameters[i]; - Console.WriteLine(other.Name); if (other.Name == parameter.Name) { return i + (method.HasThis ? 1 : 0); diff --git a/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs index 0133ac4..1158082 100644 --- a/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs +++ b/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs @@ -5,31 +5,39 @@ namespace Compiler.CodeAnalysis.Symbols { public sealed class FieldSymbol : MemberSymbol { + internal BoundExpression Initializer { get; } public override MemberKind MemberKind => MemberKind.Field; + private FieldSymbol(SyntaxNode? syntax, string name, bool isReadOnly, bool isStatic, + TypeSymbol receiverType, TypeSymbol type, - BoundConstant? constant) - : base(syntax, name, isReadOnly, isStatic, type, constant) + BoundExpression initializer) + : base(syntax, name, isReadOnly, isStatic, receiverType, type, initializer.ConstantValue) { + Initializer = initializer; } - internal FieldSymbol(VariableSymbol variable) + internal FieldSymbol(VariableSymbol variable, + TypeSymbol receiverType, + BoundExpression initializer) : this(variable.Syntax, variable.Name, variable.IsReadOnly, variable.IsStatic, + receiverType, variable.Type, - variable.Constant) + initializer) { } - internal FieldSymbol(BoundVariableDeclarationStatement declaration) - : this(declaration.Variable) + internal FieldSymbol(BoundVariableDeclarationStatement declaration, + TypeSymbol receiverType) + : this(declaration.Variable, receiverType, declaration.Initializer) { } } diff --git a/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs index e7f53c7..5917278 100644 --- a/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs +++ b/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs @@ -10,7 +10,6 @@ public sealed class FunctionSymbol : MemberSymbol public ImmutableArray Parameters { get; } public TypeSymbol ReturnType { get; } public FunctionDeclarationSyntax? Declaration { get; } - public TypeSymbol? Receiver { get; } public override SymbolKind Kind => SymbolKind.Function; public override MemberKind MemberKind => MemberKind.Method; @@ -36,14 +35,12 @@ internal FunctionSymbol(string name, FunctionDeclarationSyntax? declaration = null, IBoundScope? parentScope = null, TypeSymbol? receiver = null) - : base(declaration?.Identifier, name, true, false, type, null) + : base(declaration?.Identifier, name, true, false, receiver, type, null) { Parameters = parameters; ReturnType = type; Declaration = declaration; - Receiver = receiver; OverloadsBuilder = new List(); - if (parentScope!= null) { BoundScope = new FunctionBoundScope(receiver, this, parentScope); diff --git a/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs index 40467af..f390853 100644 --- a/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs +++ b/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs @@ -7,6 +7,12 @@ public abstract class MemberSymbol : TypedSymbol { public bool IsReadOnly { get; } public bool IsStatic { get; } + // TODO - HACK: + // Should not be nullable + // For now it's because functions can be global statements + // In the future they will only be valid inside classes/structs + // and this will be fixed + public TypeSymbol? ReceiverType { get; } public abstract MemberKind MemberKind { get; } public override SymbolKind Kind => SymbolKind.Member; @@ -14,12 +20,14 @@ private protected MemberSymbol(SyntaxNode? syntax, string name, bool isReadOnly, bool isStatic, + TypeSymbol? receiverType, TypeSymbol type, BoundConstant? constant) : base(syntax, name, type, isReadOnly ? constant : null) { IsReadOnly = isReadOnly; IsStatic = isStatic; + ReceiverType = receiverType; } } } \ No newline at end of file diff --git a/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs b/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs index bd0a2df..7f4fac5 100644 --- a/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs +++ b/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs @@ -4,14 +4,14 @@ namespace Compiler.CodeAnalysis.Syntax { public sealed partial class MemberAccessExpressionSyntax : NameExpressionSyntax { - public ExpressionSyntax ParentExpression { get; } + public NameExpressionSyntax ParentExpression { get; } public SyntaxToken OperatorToken { get; } [DiscardFromChildren] public NameExpressionSyntax MemberExpression { get; } public override SyntaxKind Kind => SyntaxKind.MemberAccessExpression; internal MemberAccessExpressionSyntax(SyntaxTree syntaxTree, - ExpressionSyntax parentExpression, + NameExpressionSyntax parentExpression, SyntaxToken operatorToken, NameExpressionSyntax memberExpression) : base(syntaxTree, memberExpression.IdentifierToken)