From 70ed34f2c21389b514d62799f55d593295717d41 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Wed, 29 Nov 2023 19:17:04 +0100 Subject: [PATCH 01/59] Update Syntax.xml --- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index a34baeb51..b2da9a0e9 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -226,6 +226,148 @@ + + + A class declaration. + + + + + The visibility modifier keyword possibly starting the declaration. + + + + + + + + + A modifier for making a class a value-type. + + + + + + + + The 'class' keyword starting the delcaration. + + + + + + + + The name of the declared class. + + + + + + + + The list of generic parameters, in case the class introduces generics. + + + + + + The primary constructor of the class. + + + + + + The body of the class. + + + + + + + A primary constructor declaration. + + + + + The optional visibility modifier keyword of the constructor. + + + + + + + + + The opening parenthesis before the parameter list. + + + + + + + + The parameters this constructor declares. + + + + + + The closing parenthesis after the parameter list. + + + + + + + + + A class declaration body. + + + + + + A semicolon-terminated empty class body. + + + + + The semicolon terminating the class body. + + + + + + + + + A block class body with multiple declarations within braces. + + + + + The opening brace token. + + + + + + + + All declaration syntaxes within the class. + + + + + + The closing brace token. + + + + + + A function declaration. From e9982211e179b3d21c0dfd789b4210782de0a7ea Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Wed, 29 Nov 2023 19:39:18 +0100 Subject: [PATCH 02/59] Update Syntax.xml --- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index b2da9a0e9..b35849861 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -320,6 +320,12 @@ + + + A single parameter in a primary constructor parameter list. + + + A class declaration body. From 0d837fdbc1ab9f0dea6df41d4dff6c82b17a7c85 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Wed, 29 Nov 2023 20:42:27 +0100 Subject: [PATCH 03/59] Update Syntax.xml --- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index b35849861..886c94405 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -305,7 +305,7 @@ - + The parameters this constructor declares. @@ -320,12 +320,28 @@ - + - A single parameter in a primary constructor parameter list. + A single item in a primary constructor parameter list. + + + A single parameter in a primary constructor parameter list. + + + + + + + + A single member declaration in a primary constructor parameter list. + + + + + A class declaration body. From d010bed8ea926870780fe2d205510ab276f3be60 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Wed, 29 Nov 2023 21:02:24 +0100 Subject: [PATCH 04/59] Update Syntax.xml --- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 70 +++++++++++++++---- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index 886c94405..ca811d2c1 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -305,7 +305,7 @@ - + The parameters this constructor declares. @@ -320,26 +320,72 @@ - + - A single item in a primary constructor parameter list. + A single parameter declaration in a primary constructor parameter list. - - - - A single parameter in a primary constructor parameter list. - + + + Optional modifiers for the parameter, in case it is manifested as a member. + + + + + The ellipsis token, in case it's variadic. + - + + + + + The name of the parameter. + + + + + + + The colon token between the name and the type. + + + + + + + The type of the parameter. + + - + - A single member declaration in a primary constructor parameter list. + Modifiers for a primary constructor parameter, in case it is manifested as a member. - + + + The optional visibility modifier keyword of the member. + + + + + + + + The optional field modifier keyword to mark a field instead of a property. + + + + + + + The keyword introducing the variable, either 'var' or 'val'. + + + + + From c6b52360673e632c011fb922bfde3a9869e384b3 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 30 Nov 2023 10:05:51 +0100 Subject: [PATCH 05/59] Added keywords --- src/Draco.Compiler/Api/Syntax/TokenKind.cs | 15 +++++++++++++++ src/Draco.Compiler/Internal/Syntax/Lexer.cs | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/Draco.Compiler/Api/Syntax/TokenKind.cs b/src/Draco.Compiler/Api/Syntax/TokenKind.cs index 9c6c533e5..0b5236ad7 100644 --- a/src/Draco.Compiler/Api/Syntax/TokenKind.cs +++ b/src/Draco.Compiler/Api/Syntax/TokenKind.cs @@ -80,6 +80,11 @@ public enum TokenKind /// KeywordAnd, + /// + /// The keyword 'class'. + /// + KeywordClass, + /// /// The keyword 'else'. /// @@ -90,6 +95,11 @@ public enum TokenKind /// KeywordFalse, + /// + /// The keyword 'field'. + /// + KeywordField, + /// /// The keyword 'for'. /// @@ -170,6 +180,11 @@ public enum TokenKind /// KeywordVal, + /// + /// The keyword 'value'. + /// + KeywordValue, + /// /// The keyword 'var'. /// diff --git a/src/Draco.Compiler/Internal/Syntax/Lexer.cs b/src/Draco.Compiler/Internal/Syntax/Lexer.cs index 55af5e931..2a4ce07ea 100644 --- a/src/Draco.Compiler/Internal/Syntax/Lexer.cs +++ b/src/Draco.Compiler/Internal/Syntax/Lexer.cs @@ -322,8 +322,10 @@ Unit TakeWithText(TokenKind tokenKind, int length) var tokenKind = ident switch { var _ when ident.Span.SequenceEqual("and") => TokenKind.KeywordAnd, + var _ when ident.Span.SequenceEqual("class") => TokenKind.KeywordClass, var _ when ident.Span.SequenceEqual("else") => TokenKind.KeywordElse, var _ when ident.Span.SequenceEqual("false") => TokenKind.KeywordFalse, + var _ when ident.Span.SequenceEqual("field") => TokenKind.KeywordField, var _ when ident.Span.SequenceEqual("for") => TokenKind.KeywordFor, var _ when ident.Span.SequenceEqual("func") => TokenKind.KeywordFunc, var _ when ident.Span.SequenceEqual("goto") => TokenKind.KeywordGoto, @@ -340,6 +342,7 @@ var _ when ident.Span.SequenceEqual("rem") => TokenKind.KeywordRem, var _ when ident.Span.SequenceEqual("return") => TokenKind.KeywordReturn, var _ when ident.Span.SequenceEqual("true") => TokenKind.KeywordTrue, var _ when ident.Span.SequenceEqual("val") => TokenKind.KeywordVal, + var _ when ident.Span.SequenceEqual("value") => TokenKind.KeywordValue, var _ when ident.Span.SequenceEqual("var") => TokenKind.KeywordVar, var _ when ident.Span.SequenceEqual("while") => TokenKind.KeywordWhile, _ => TokenKind.Identifier, From 0313e1f6acc98ec2ebe586d5f445dfad3c9cd88b Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 30 Nov 2023 18:59:24 +0100 Subject: [PATCH 06/59] Update Parser.cs --- src/Draco.Compiler/Internal/Syntax/Parser.cs | 44 ++++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/Draco.Compiler/Internal/Syntax/Parser.cs b/src/Draco.Compiler/Internal/Syntax/Parser.cs index 9064935b6..23fe1b1f8 100644 --- a/src/Draco.Compiler/Internal/Syntax/Parser.cs +++ b/src/Draco.Compiler/Internal/Syntax/Parser.cs @@ -153,6 +153,8 @@ private ExpressionParserDelegate BinaryRight(params TokenKind[] operators) => le private static readonly TokenKind[] declarationStarters = new[] { TokenKind.KeywordImport, + TokenKind.KeywordValue, + TokenKind.KeywordClass, TokenKind.KeywordFunc, TokenKind.KeywordModule, TokenKind.KeywordVar, @@ -257,21 +259,31 @@ internal DeclarationSyntax ParseDeclaration(bool local = false) => /// The parsed . private DeclarationSyntax ParseDeclaration(DeclarationContext context) { - var modifier = this.ParseVisibilityModifier(); + var visibility = this.ParseVisibilityModifier(); switch (this.Peek()) { case TokenKind.KeywordImport: + // TODO: What if the visibility modifier is not null here? Should that be an error? return this.ParseImportDeclaration(); + case TokenKind.KeywordValue: + { + var valueKeyword = this.Expect(TokenKind.KeywordValue); + return this.ParseClassDeclaration(visibility: visibility, valueType: valueKeyword); + } + + case TokenKind.KeywordClass: + return this.ParseClassDeclaration(visibility: visibility, valueType: null); + case TokenKind.KeywordFunc: - return this.ParseFunctionDeclaration(modifier); + return this.ParseFunctionDeclaration(visibility); case TokenKind.KeywordModule: return this.ParseModuleDeclaration(context); case TokenKind.KeywordVar: case TokenKind.KeywordVal: - return this.ParseVariableDeclaration(modifier); + return this.ParseVariableDeclaration(visibility); case TokenKind.Identifier when this.Peek(1) == TokenKind.Colon: return this.ParseLabelDeclaration(context); @@ -286,7 +298,7 @@ _ when IsVisibilityModifier(t) => false, }); var info = DiagnosticInfo.Create(SyntaxErrors.UnexpectedInput, formatArgs: "declaration"); var diag = new SyntaxDiagnosticInfo(info, Offset: 0, Width: input.FullWidth); - var node = new UnexpectedDeclarationSyntax(modifier, input); + var node = new UnexpectedDeclarationSyntax(visibility, input); this.AddDiagnostic(node, diag); return node; } @@ -387,9 +399,33 @@ private VariableDeclarationSyntax ParseVariableDeclaration(SyntaxToken? visibili return new VariableDeclarationSyntax(visibility, keyword, identifier, type, assignment, semicolon); } + /// + /// Parses a class declaration. + /// + /// Optional visibility modifier token. + /// Optional valuetype modifier token. + /// The parsed . + private ClassDeclarationSyntax ParseClassDeclaration(SyntaxToken? visibility, SyntaxToken? valueType) + { + // Class keyword and name of the class + var classKeyword = this.Expect(TokenKind.KeywordClass); + var name = this.Expect(TokenKind.Identifier); + + // Optional generics + var generics = null as GenericParameterListSyntax; + if (this.Peek() == TokenKind.LessThan) generics = this.ParseGenericParameterList(); + + // TODO: Parse optional primary constructor + + // TODO: Parse body + + throw new NotImplementedException(); + } + /// /// Parses a function declaration. /// + /// Optional visibility modifier token. /// The parsed . private FunctionDeclarationSyntax ParseFunctionDeclaration(SyntaxToken? visibility) { From ad6f2908b553deb7fe47743c9234c084a3617938 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 30 Nov 2023 20:57:41 +0100 Subject: [PATCH 07/59] Update Parser.cs --- src/Draco.Compiler/Internal/Syntax/Parser.cs | 101 ++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/Syntax/Parser.cs b/src/Draco.Compiler/Internal/Syntax/Parser.cs index 23fe1b1f8..17d7ca669 100644 --- a/src/Draco.Compiler/Internal/Syntax/Parser.cs +++ b/src/Draco.Compiler/Internal/Syntax/Parser.cs @@ -415,10 +415,107 @@ private ClassDeclarationSyntax ParseClassDeclaration(SyntaxToken? visibility, Sy var generics = null as GenericParameterListSyntax; if (this.Peek() == TokenKind.LessThan) generics = this.ParseGenericParameterList(); - // TODO: Parse optional primary constructor + var primaryCtor = null as PrimaryConstructorSyntax; + switch (this.Peek()) + { + case TokenKind.ParenOpen: + case TokenKind.KeywordPublic: + case TokenKind.KeywordInternal: + primaryCtor = this.ParsePrimaryConstructor(); + break; + } + + var body = this.ParseClassBody(); + + return new ClassDeclarationSyntax( + visibility, + valueType, + classKeyword, + name, + generics, + primaryCtor, + body); + } + + /// + /// Parses a primary constructor, including the visibility modifier and parentheses. + /// + /// The parsed . + private PrimaryConstructorSyntax ParsePrimaryConstructor() + { + var visibility = this.ParseVisibilityModifier(); + + var openParen = this.Expect(TokenKind.ParenOpen); + var ctorParameters = this.ParseSeparatedSyntaxList( + elementParser: this.ParsePrimaryConstructorParameter, + separatorKind: TokenKind.Comma, + stopKind: TokenKind.ParenClose); + var closeParen = this.Expect(TokenKind.ParenClose); - // TODO: Parse body + return new PrimaryConstructorSyntax( + visibility, + openParen, + ctorParameters, + closeParen); + } + /// + /// Parses a primary constructor parameter. + /// + /// The parsed . + private PrimaryConstructorParameterSyntax ParsePrimaryConstructorParameter() + { + // TODO + throw new NotImplementedException(); + } + + /// + /// Parses the modifiers of a primary constructor parameter that could make it a member. + /// Can return null, if the parameter is not a member. + /// + /// The parsed , or null + /// if the parameter is not a member. + private PrimaryConstructorParameterModifiersSyntax? ParsePrimaryConstructorParameterModifiers() + { + // TODO + throw new NotImplementedException(); + } + + /// + /// Parses the body of a class. + /// + /// The parsed . + private ClassBodySyntax ParseClassBody() + { + switch (this.Peek()) + { + case TokenKind.Semicolon: + return this.ParseEmptyClassBody(); + case TokenKind.CurlyOpen: + return this.ParseBlockClassBody(); + default: + // TODO + throw new NotImplementedException(); + } + } + + /// + /// Parses an empty class body, which is just a semicolon. + /// + /// The parsed . + private EmptyClassBodySyntax ParseEmptyClassBody() + { + var semicolon = this.Expect(TokenKind.Semicolon); + return new EmptyClassBodySyntax(semicolon); + } + + /// + /// Parses a block class body declared with curly braces. + /// + /// The parsed . + private BlockClassBodySyntax ParseBlockClassBody() + { + // TODO throw new NotImplementedException(); } From c9751991e0341313703c8007a1ebc2eb464f88cd Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Fri, 1 Dec 2023 13:27:42 +0100 Subject: [PATCH 08/59] Parsing mostly done --- src/Draco.Compiler/Internal/Syntax/Parser.cs | 53 ++++++++++++++++--- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 4 +- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/Draco.Compiler/Internal/Syntax/Parser.cs b/src/Draco.Compiler/Internal/Syntax/Parser.cs index 17d7ca669..7f2458501 100644 --- a/src/Draco.Compiler/Internal/Syntax/Parser.cs +++ b/src/Draco.Compiler/Internal/Syntax/Parser.cs @@ -465,8 +465,17 @@ private PrimaryConstructorSyntax ParsePrimaryConstructor() /// The parsed . private PrimaryConstructorParameterSyntax ParsePrimaryConstructorParameter() { - // TODO - throw new NotImplementedException(); + var memberModifiers = this.ParsePrimaryConstructorParameterMemberModifiers(); + this.Matches(TokenKind.Ellipsis, out var variadic); + var name = this.Expect(TokenKind.Identifier); + var colon = this.Expect(TokenKind.Colon); + var type = this.ParseType(); + return new( + memberModifiers, + variadic, + name, + colon, + type); } /// @@ -475,10 +484,29 @@ private PrimaryConstructorParameterSyntax ParsePrimaryConstructorParameter() /// /// The parsed , or null /// if the parameter is not a member. - private PrimaryConstructorParameterModifiersSyntax? ParsePrimaryConstructorParameterModifiers() + private PrimaryConstructorParameterMemberModifiersSyntax? ParsePrimaryConstructorParameterMemberModifiers() { - // TODO - throw new NotImplementedException(); + var visibilityModifier = this.ParseVisibilityModifier(); + this.Matches(TokenKind.KeywordField, out var fieldToken); + + var peek = this.Peek(); + if (peek is TokenKind.KeywordVar or TokenKind.KeywordVal) + { + var keyword = this.Advance(); + return new PrimaryConstructorParameterMemberModifiersSyntax( + visibilityModifier, + fieldToken, + keyword); + } + else if (visibilityModifier is not null || fieldToken is not null) + { + // TODO: Error + throw new NotImplementedException(); + } + else + { + return null; + } } /// @@ -515,8 +543,19 @@ private EmptyClassBodySyntax ParseEmptyClassBody() /// The parsed . private BlockClassBodySyntax ParseBlockClassBody() { - // TODO - throw new NotImplementedException(); + var openBrace = this.Expect(TokenKind.CurlyOpen); + var decls = SyntaxList.CreateBuilder(); + while (true) + { + // Break on the end of the block + if (this.Peek() is TokenKind.EndOfInput or TokenKind.CurlyClose) break; + + // Parse a declaration + var decl = this.ParseDeclaration(DeclarationContext.Global); + decls.Add(decl); + } + var closeBrace = this.Expect(TokenKind.CurlyClose); + return new(openBrace, decls.ToSyntaxList(), closeBrace); } /// diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index ca811d2c1..61204c557 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -325,7 +325,7 @@ A single parameter declaration in a primary constructor parameter list. - + Optional modifiers for the parameter, in case it is manifested as a member. @@ -358,7 +358,7 @@ - + Modifiers for a primary constructor parameter, in case it is manifested as a member. From a9e6ca855919a7d30e8847f9bd38e8c82b9cfbf1 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Fri, 1 Dec 2023 13:31:54 +0100 Subject: [PATCH 09/59] Update SyntaxFacts.cs --- src/Draco.Compiler/Api/Syntax/SyntaxFacts.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Draco.Compiler/Api/Syntax/SyntaxFacts.cs b/src/Draco.Compiler/Api/Syntax/SyntaxFacts.cs index d51a7bde5..26ed66ea8 100644 --- a/src/Draco.Compiler/Api/Syntax/SyntaxFacts.cs +++ b/src/Draco.Compiler/Api/Syntax/SyntaxFacts.cs @@ -18,8 +18,10 @@ public static class SyntaxFacts TokenKind.EndOfInput => string.Empty, TokenKind.InterpolationEnd => "}", TokenKind.KeywordAnd => "and", + TokenKind.KeywordClass => "class", TokenKind.KeywordElse => "else", TokenKind.KeywordFalse => "false", + TokenKind.KeywordField => "field", TokenKind.KeywordFor => "for", TokenKind.KeywordFunc => "func", TokenKind.KeywordGoto => "goto", @@ -36,6 +38,7 @@ public static class SyntaxFacts TokenKind.KeywordReturn => "return", TokenKind.KeywordTrue => "true", TokenKind.KeywordVal => "val", + TokenKind.KeywordValue => "value", TokenKind.KeywordVar => "var", TokenKind.KeywordWhile => "while", TokenKind.ParenOpen => "(", From df07111ee8aa1e7ff3daf8c448f153f3bd98385b Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 2 Dec 2023 09:02:38 +0100 Subject: [PATCH 10/59] Added class declaration --- .../Internal/Declarations/ClassDeclaration.cs | 54 +++++++++++++++++++ .../Declarations/SingleModuleDeclaration.cs | 1 + 2 files changed, 55 insertions(+) create mode 100644 src/Draco.Compiler/Internal/Declarations/ClassDeclaration.cs diff --git a/src/Draco.Compiler/Internal/Declarations/ClassDeclaration.cs b/src/Draco.Compiler/Internal/Declarations/ClassDeclaration.cs new file mode 100644 index 000000000..0f1b63721 --- /dev/null +++ b/src/Draco.Compiler/Internal/Declarations/ClassDeclaration.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Draco.Compiler.Api.Syntax; + +namespace Draco.Compiler.Internal.Declarations; + +/// +/// A class declaration. +/// +internal sealed class ClassDeclaration : Declaration +{ + /// + /// The syntax of the declaration. + /// + public ClassDeclarationSyntax Syntax { get; } + + public override ImmutableArray Children => + InterlockedUtils.InitializeDefault(ref this.children, this.BuildChildren); + private ImmutableArray children; + + public override IEnumerable DeclaringSyntaxes + { + get + { + yield return this.Syntax; + } + } + + public ClassDeclaration(ClassDeclarationSyntax syntax) + : base(syntax.Name.Text) + { + this.Syntax = syntax; + } + + private ImmutableArray BuildChildren() + { + if (this.Syntax.Body is not BlockClassBodySyntax block) return ImmutableArray.Empty; + + return block.Declarations.Select(this.BuildChild).OfType().ToImmutableArray(); + } + + // TODO: More entries to handle + private Declaration? BuildChild(SyntaxNode node) => node switch + { + // NOTE: We ignore import declarations in the declaration tree, unlike Roslyn + // We handle import declarations during constructing the binders + // Since we allow for imports in local scopes too, this is the most sensible choice + ImportDeclarationSyntax => null, + UnexpectedDeclarationSyntax => null, + _ => throw new ArgumentOutOfRangeException(nameof(node)), + }; +} diff --git a/src/Draco.Compiler/Internal/Declarations/SingleModuleDeclaration.cs b/src/Draco.Compiler/Internal/Declarations/SingleModuleDeclaration.cs index 57bc68146..93067f721 100644 --- a/src/Draco.Compiler/Internal/Declarations/SingleModuleDeclaration.cs +++ b/src/Draco.Compiler/Internal/Declarations/SingleModuleDeclaration.cs @@ -52,6 +52,7 @@ private ImmutableArray BuildChildren() => VariableDeclarationSyntax var => new GlobalDeclaration(var), FunctionDeclarationSyntax func => new FunctionDeclaration(func), ModuleDeclarationSyntax module => new SingleModuleDeclaration(module.Name.Text, this.Path.Append(module.Name.Text), module), + ClassDeclarationSyntax cls => new ClassDeclaration(cls), UnexpectedDeclarationSyntax => null, _ => throw new ArgumentOutOfRangeException(nameof(node)), }; From c29ac8b27ca285adbe0445522ab4d5024fcc2455 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 2 Dec 2023 11:22:15 +0100 Subject: [PATCH 11/59] Added basic class symbol --- .../Symbols/Source/SourceClassSymbol.cs | 37 +++++++++++++++++++ .../Symbols/Source/SourceModuleSymbol.cs | 2 + 2 files changed, 39 insertions(+) create mode 100644 src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs new file mode 100644 index 000000000..258448f51 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api.Syntax; +using Draco.Compiler.Internal.Declarations; +using Draco.Compiler.Internal.Documentation; +using Draco.Compiler.Internal.Documentation.Extractors; + +namespace Draco.Compiler.Internal.Symbols.Source; + +/// +/// Represents a class from source code. +/// +internal sealed class SourceClassSymbol : TypeSymbol +{ + public override Symbol ContainingSymbol { get; } + + public override ClassDeclarationSyntax DeclaringSyntax => this.declaration.Syntax; + + public override SymbolDocumentation Documentation => InterlockedUtils.InitializeNull(ref this.documentation, this.BuildDocumentation); + private SymbolDocumentation? documentation; + + private readonly ClassDeclaration declaration; + + public SourceClassSymbol(Symbol containingSymbol, ClassDeclaration declaration) + { + this.ContainingSymbol = containingSymbol; + this.declaration = declaration; + } + + public override string ToString() => this.DeclaringSyntax.Name.Text; + + private SymbolDocumentation BuildDocumentation() => + MarkdownDocumentationExtractor.Extract(this); +} diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs index c5dcfa9cd..dcf8e8d34 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs @@ -99,11 +99,13 @@ private ImmutableArray BindMembers(IBinderProvider binderProvider) FunctionDeclaration f => this.BuildFunction(f), GlobalDeclaration g => this.BuildGlobal(g), MergedModuleDeclaration m => this.BuildModule(m), + ClassDeclaration c => this.BuildClass(c), _ => throw new ArgumentOutOfRangeException(nameof(declaration)), }; private FunctionSymbol BuildFunction(FunctionDeclaration declaration) => new SourceFunctionSymbol(this, declaration); private GlobalSymbol BuildGlobal(GlobalDeclaration declaration) => new SourceGlobalSymbol(this, declaration); + private TypeSymbol BuildClass(ClassDeclaration declaration) => new SourceClassSymbol(this, declaration); private ModuleSymbol BuildModule(MergedModuleDeclaration declaration) => new SourceModuleSymbol(this.DeclaringCompilation, this, declaration); private SymbolDocumentation BuildDocumentation() => From d6572c3199af7ee01449dc933d9a7fcfdc8a2002 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 2 Dec 2023 11:32:34 +0100 Subject: [PATCH 12/59] Added a devhost project for easier testing --- .../Draco.Compiler.DevHost.csproj | 18 ++ src/Draco.Compiler.DevHost/Program.cs | 223 ++++++++++++++++++ src/Draco.sln | 10 +- 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/Draco.Compiler.DevHost/Draco.Compiler.DevHost.csproj create mode 100644 src/Draco.Compiler.DevHost/Program.cs diff --git a/src/Draco.Compiler.DevHost/Draco.Compiler.DevHost.csproj b/src/Draco.Compiler.DevHost/Draco.Compiler.DevHost.csproj new file mode 100644 index 000000000..4783564fd --- /dev/null +++ b/src/Draco.Compiler.DevHost/Draco.Compiler.DevHost.csproj @@ -0,0 +1,18 @@ + + + + Exe + net7.0 + enable + + + + + + + + + + + + diff --git a/src/Draco.Compiler.DevHost/Program.cs b/src/Draco.Compiler.DevHost/Program.cs new file mode 100644 index 000000000..4fedeee29 --- /dev/null +++ b/src/Draco.Compiler.DevHost/Program.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.CommandLine; +using System.IO; +using System.Linq; +using Draco.Compiler.Api; +using Draco.Compiler.Api.Diagnostics; +using Draco.Compiler.Api.Scripting; +using Draco.Compiler.Api.Syntax; + +namespace Draco.Compiler.DevHost; + +internal class Program +{ + private static IEnumerable BclReferences => Basic.Reference.Assemblies.Net70.ReferenceInfos.All + .Select(r => MetadataReference.FromPeStream(new MemoryStream(r.ImageBytes))); + + internal static int Main(string[] args) => + ConfigureCommands().Invoke(args); + + private static RootCommand ConfigureCommands() + { + var fileArgument = new Argument("source file", description: "The Draco source file"); + var outputOption = new Option(new string[] { "-o", "--output" }, () => new FileInfo("output"), "Specifies the output file"); + var optionalOutputOption = new Option(new string[] { "-o", "--output" }, () => null, "Specifies the (optional) output file"); + var referencesOption = new Option(new string[] { "-r", "--reference" }, Array.Empty, "Specifies additional assembly references to use when compiling"); + var filesArgument = new Argument("source files", Array.Empty, "Specifies draco source files that should be compiled"); + var rootModuleOption = new Option(new string[] { "-m", "--root-module" }, () => null, "Specifies the root module folder of the compiled files"); + + // Compile + + var compileCommand = new Command("compile", "Compiles the Draco program") + { + filesArgument, + outputOption, + rootModuleOption, + referencesOption, + }; + compileCommand.SetHandler(CompileCommand, filesArgument, outputOption, rootModuleOption, referencesOption); + + // Run + + var runCommand = new Command("run", "Runs the Draco program") + { + filesArgument, + rootModuleOption, + referencesOption, + }; + runCommand.SetHandler(RunCommand, filesArgument, rootModuleOption, referencesOption); + + // IR code + + var irCommand = new Command("ir", "Generates the intermediate-representation of the Draco program") + { + filesArgument, + rootModuleOption, + optionalOutputOption, + }; + irCommand.SetHandler(IrCommand, filesArgument, rootModuleOption, optionalOutputOption); + + // Symbol tree + + var symbolsCommand = new Command("symbols", "Prints the symbol-tree of the program") + { + filesArgument, + rootModuleOption, + optionalOutputOption, + }; + symbolsCommand.SetHandler(SymbolsCommand, filesArgument, rootModuleOption, optionalOutputOption); + + // Declaration tree + + var declarationsCommand = new Command("declarations", "Prints the declarations-tree of the program") + { + filesArgument, + rootModuleOption, + optionalOutputOption, + }; + declarationsCommand.SetHandler(DeclarationsCommand, filesArgument, rootModuleOption, optionalOutputOption); + + // Formatting + + var formatCommand = new Command("format", "Formats contents of the specified Draco file") + { + fileArgument, + optionalOutputOption, + }; + formatCommand.SetHandler(FormatCommand, fileArgument, optionalOutputOption); + + return new RootCommand("CLI for the Draco compiler") + { + compileCommand, + runCommand, + irCommand, + symbolsCommand, + declarationsCommand, + formatCommand + }; + } + + private static void CompileCommand(FileInfo[] input, FileInfo output, DirectoryInfo? rootModule, FileInfo[] references) + { + var syntaxTrees = GetSyntaxTrees(input); + var (path, name) = ExtractOutputPathAndName(output); + var compilation = Compilation.Create( + syntaxTrees: syntaxTrees, + metadataReferences: references + .Select(r => MetadataReference.FromPeStream(r.OpenRead())) + .Concat(BclReferences) + .ToImmutableArray(), + rootModulePath: rootModule?.FullName, + outputPath: path, + assemblyName: name); + using var peStream = new FileStream(Path.ChangeExtension(output.FullName, ".dll"), FileMode.OpenOrCreate); + using var pdbStream = new FileStream(Path.ChangeExtension(output.FullName, ".pdb"), FileMode.OpenOrCreate); + var emitResult = compilation.Emit( + peStream: peStream, + pdbStream: pdbStream); + EmitDiagnostics(emitResult); + } + + private static void RunCommand(FileInfo[] input, DirectoryInfo? rootModule, FileInfo[] references) + { + var syntaxTrees = GetSyntaxTrees(input); + var compilation = Compilation.Create( + syntaxTrees: syntaxTrees, + metadataReferences: references + .Select(r => MetadataReference.FromPeStream(r.OpenRead())) + .Concat(BclReferences) + .ToImmutableArray(), + rootModulePath: rootModule?.FullName); + var execResult = ScriptingEngine.Execute(compilation); + if (!EmitDiagnostics(execResult)) + { + Console.WriteLine($"Result: {execResult.Result}"); + } + } + + private static void IrCommand(FileInfo[] input, DirectoryInfo? rootModule, FileInfo? output) + { + var syntaxTrees = GetSyntaxTrees(input); + var compilation = Compilation.Create( + syntaxTrees: syntaxTrees, + rootModulePath: rootModule?.FullName); + using var irStream = OpenOutputOrStdout(output); + var emitResult = compilation.Emit(irStream: irStream); + EmitDiagnostics(emitResult); + } + + private static void SymbolsCommand(FileInfo[] input, DirectoryInfo? rootModule, FileInfo? output) + { + var syntaxTrees = GetSyntaxTrees(input); + var compilation = Compilation.Create( + syntaxTrees: syntaxTrees, + rootModulePath: rootModule?.FullName); + using var symbolsStream = OpenOutputOrStdout(output); + var emitResult = compilation.Emit(symbolTreeStream: symbolsStream); + EmitDiagnostics(emitResult); + } + + private static void DeclarationsCommand(FileInfo[] input, DirectoryInfo? rootModule, FileInfo? output) + { + var syntaxTrees = GetSyntaxTrees(input); + var compilation = Compilation.Create( + syntaxTrees: syntaxTrees, + rootModulePath: rootModule?.FullName); + using var declarationStream = OpenOutputOrStdout(output); + var emitResult = compilation.Emit(declarationTreeStream: declarationStream); + EmitDiagnostics(emitResult); + } + + private static void FormatCommand(FileInfo input, FileInfo? output) + { + var syntaxTree = GetSyntaxTrees(input).First(); + using var outputStream = OpenOutputOrStdout(output); + new StreamWriter(outputStream).Write(syntaxTree.Format().ToString()); + } + + private static ImmutableArray GetSyntaxTrees(params FileInfo[] input) + { + var result = ImmutableArray.CreateBuilder(); + foreach (var file in input) + { + var sourceText = SourceText.FromFile(file.FullName); + result.Add(SyntaxTree.Parse(sourceText)); + } + return result.ToImmutable(); + } + + private static bool EmitDiagnostics(EmitResult result) + { + if (result.Success) return false; + foreach (var diag in result.Diagnostics) + { + Console.Error.WriteLine(diag.ToString()); + } + return true; + } + + private static bool EmitDiagnostics(ExecutionResult result) + { + if (result.Success) return false; + foreach (var diag in result.Diagnostics) + { + Console.Error.WriteLine(diag.ToString()); + } + return true; + } + + private static (string Path, string Name) ExtractOutputPathAndName(FileInfo outputInfo) + { + var outputPath = outputInfo.FullName; + var path = Path.GetDirectoryName(outputPath) ?? string.Empty; + path = Path.GetFullPath(path); + var name = Path.GetFileNameWithoutExtension(outputPath) ?? string.Empty; + return (path, name); + } + + private static Stream OpenOutputOrStdout(FileInfo? output) => output is null + ? Console.OpenStandardOutput() + : output.Open(FileMode.OpenOrCreate, FileAccess.Write); +} diff --git a/src/Draco.sln b/src/Draco.sln index 1393cbc6b..08b2847ed 100644 --- a/src/Draco.sln +++ b/src/Draco.sln @@ -44,7 +44,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Draco.DebugAdapter", "Draco EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Draco.Compiler.Benchmarks", "Draco.Compiler.Benchmarks\Draco.Compiler.Benchmarks.csproj", "{067C2F6D-8C70-4BA6-83BE-F7B351D09188}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Draco.JsonRpc", "Draco.JsonRpc\Draco.JsonRpc.csproj", "{5C56C907-C614-49EE-B433-D88CB9CE8983}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Draco.JsonRpc", "Draco.JsonRpc\Draco.JsonRpc.csproj", "{5C56C907-C614-49EE-B433-D88CB9CE8983}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Draco.Compiler.DevHost", "Draco.Compiler.DevHost\Draco.Compiler.DevHost.csproj", "{A973B164-07A0-45C3-AA48-DFFDDEC51980}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -161,6 +163,12 @@ Global {5C56C907-C614-49EE-B433-D88CB9CE8983}.Nuget|Any CPU.Build.0 = Debug|Any CPU {5C56C907-C614-49EE-B433-D88CB9CE8983}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C56C907-C614-49EE-B433-D88CB9CE8983}.Release|Any CPU.Build.0 = Release|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Nuget|Any CPU.ActiveCfg = Debug|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Nuget|Any CPU.Build.0 = Debug|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A973B164-07A0-45C3-AA48-DFFDDEC51980}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 96add9affc7ef9fd03fc5144a2d36a2ef69720be Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 2 Dec 2023 18:27:25 +0100 Subject: [PATCH 13/59] Update SourceClassSymbol.cs --- .../Symbols/Source/SourceClassSymbol.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 258448f51..a8a4d34f5 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using Draco.Compiler.Internal.Declarations; using Draco.Compiler.Internal.Documentation; using Draco.Compiler.Internal.Documentation.Extractors; +using Draco.Compiler.Internal.Symbols.Metadata; namespace Draco.Compiler.Internal.Symbols.Source; @@ -15,6 +17,19 @@ namespace Draco.Compiler.Internal.Symbols.Source; /// internal sealed class SourceClassSymbol : TypeSymbol { + // TODO: Defined members + + public override string Name => this.DeclaringSyntax.Name.Text; + + public override Api.Semantics.Visibility Visibility => + GetVisibilityFromTokenKind(this.DeclaringSyntax.VisibilityModifier?.Kind); + + public override ImmutableArray GenericParameters => + InterlockedUtils.InitializeDefault(ref this.genericParameters, this.BuildGenericParameters); + private ImmutableArray genericParameters; + + public override bool IsValueType => this.DeclaringSyntax.ValueModifier is not null; + public override Symbol ContainingSymbol { get; } public override ClassDeclarationSyntax DeclaringSyntax => this.declaration.Syntax; @@ -32,6 +47,17 @@ public SourceClassSymbol(Symbol containingSymbol, ClassDeclaration declaration) public override string ToString() => this.DeclaringSyntax.Name.Text; + private ImmutableArray BuildGenericParameters() + { + var genericParams = this.DeclaringSyntax.Generics; + if (genericParams is null) return ImmutableArray.Empty; + + return genericParams.Parameters.Values + .Select(syntax => new SourceTypeParameterSymbol(this, syntax)) + .Cast() + .ToImmutableArray(); + } + private SymbolDocumentation BuildDocumentation() => MarkdownDocumentationExtractor.Extract(this); } From 968fbf54e7c0571be56dd273b0d090750c0d68ea Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 2 Dec 2023 21:28:14 +0100 Subject: [PATCH 14/59] Update MetadataSymbol.cs --- .../Internal/Symbols/Metadata/MetadataSymbol.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs index e3af4ac36..3a1bfad5e 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs @@ -45,7 +45,7 @@ public static IEnumerable ToSymbol( if (methodName != ".ctor") continue; // This is a constructor, synthetize a function overload - var ctor = SynthetizeConstructor(typeSymbol, method); + var ctor = new MetadataConstructorFunctionSymbol(typeSymbol, method); results.Add(ctor); } } @@ -106,10 +106,6 @@ public static IEnumerable ToSymbol( }; } - private static FunctionSymbol SynthetizeConstructor( - MetadataTypeSymbol type, - MethodDefinition ctorMethod) => new MetadataConstructorFunctionSymbol(type, ctorMethod); - /// /// Gets the documentation XML as text for the given . /// From d93d474127e84079f5e8a59bdbeb728d990a219c Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 12:52:34 +0100 Subject: [PATCH 15/59] Update SourceClassSymbol.cs --- .../Symbols/Source/SourceClassSymbol.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index a8a4d34f5..e7ebad281 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Declarations; using Draco.Compiler.Internal.Documentation; using Draco.Compiler.Internal.Documentation.Extractors; -using Draco.Compiler.Internal.Symbols.Metadata; namespace Draco.Compiler.Internal.Symbols.Source; @@ -17,7 +14,9 @@ namespace Draco.Compiler.Internal.Symbols.Source; /// internal sealed class SourceClassSymbol : TypeSymbol { - // TODO: Defined members + public override IEnumerable DefinedMembers => + InterlockedUtils.InitializeDefault(ref this.definedMembers, this.BuildMembers); + private ImmutableArray definedMembers; public override string Name => this.DeclaringSyntax.Name.Text; @@ -58,6 +57,22 @@ private ImmutableArray BuildGenericParameters() .ToImmutableArray(); } + private ImmutableArray BuildMembers() + { + var result = ImmutableArray.CreateBuilder(); + + // TODO: Check for secondary constructors + // If there is no constructor, add a default one + if (this.DeclaringSyntax.PrimaryConstructor is null) + { + // TODO: add a default constructor + throw new NotImplementedException(); + } + + // Done + return result.ToImmutable(); + } + private SymbolDocumentation BuildDocumentation() => MarkdownDocumentationExtractor.Extract(this); } From b093f5c63caf1d5f6f7935422dc2edc9d070921f Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 13:01:07 +0100 Subject: [PATCH 16/59] Added default ctor symbol --- .../Symbols/Source/SourceClassSymbol.cs | 4 +-- .../Synthetized/DefaultConstructorSymbol.cs | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index e7ebad281..1c2548d86 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -6,6 +6,7 @@ using Draco.Compiler.Internal.Declarations; using Draco.Compiler.Internal.Documentation; using Draco.Compiler.Internal.Documentation.Extractors; +using Draco.Compiler.Internal.Symbols.Synthetized; namespace Draco.Compiler.Internal.Symbols.Source; @@ -65,8 +66,7 @@ private ImmutableArray BuildMembers() // If there is no constructor, add a default one if (this.DeclaringSyntax.PrimaryConstructor is null) { - // TODO: add a default constructor - throw new NotImplementedException(); + result.Add(new DefaultConstructorSymbol(this)); } // Done diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs new file mode 100644 index 000000000..2fa33e64f --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Draco.Compiler.Internal.Symbols.Synthetized; + +/// +/// A default constructor for in-source types. +/// +internal sealed class DefaultConstructorSymbol : IrFunctionSymbol +{ + public override Symbol ContainingSymbol { get; } + + public override CodegenDelegate Codegen => (codegen, target, args) => + { + // TODO + throw new NotImplementedException(); + }; + + public override ImmutableArray Parameters => ImmutableArray.Empty; + public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + public override bool IsStatic => false; + + public DefaultConstructorSymbol(Symbol containingSymbol) + { + this.ContainingSymbol = containingSymbol; + } +} From e3c68757251e7c5574a1ea057dcbc45168e0d582 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 13:03:03 +0100 Subject: [PATCH 17/59] Update Program.cs --- src/Draco.Compiler.DevHost/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Draco.Compiler.DevHost/Program.cs b/src/Draco.Compiler.DevHost/Program.cs index 4fedeee29..8053a7650 100644 --- a/src/Draco.Compiler.DevHost/Program.cs +++ b/src/Draco.Compiler.DevHost/Program.cs @@ -8,6 +8,7 @@ using Draco.Compiler.Api.Diagnostics; using Draco.Compiler.Api.Scripting; using Draco.Compiler.Api.Syntax; +using static Basic.Reference.Assemblies.Net70; namespace Draco.Compiler.DevHost; @@ -142,6 +143,8 @@ private static void IrCommand(FileInfo[] input, DirectoryInfo? rootModule, FileI var syntaxTrees = GetSyntaxTrees(input); var compilation = Compilation.Create( syntaxTrees: syntaxTrees, + // TODO: Add references from CLI? + metadataReferences: BclReferences.ToImmutableArray(), rootModulePath: rootModule?.FullName); using var irStream = OpenOutputOrStdout(output); var emitResult = compilation.Emit(irStream: irStream); From 295e607a60e0059a25f8144f8e236724f03929a9 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 13:17:47 +0100 Subject: [PATCH 18/59] Create IClass.cs --- .../Internal/OptimizingIr/Model/IClass.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs new file mode 100644 index 000000000..e2baf87da --- /dev/null +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api.Semantics; + +namespace Draco.Compiler.Internal.OptimizingIr.Model; + +/// +/// Read-only interface of a class. +/// +internal interface IClass +{ + /// + /// The symbol that corresponds to this class. + /// + public TypeSymbol Symbol { get; } + + /// + /// The name of this class. + /// + public string Name { get; } + + /// + /// The module this class is defined in. + /// + public IModule DeclaringModule { get; } + + /// + /// The assembly this class is defined in. + /// + public IAssembly Assembly { get; } + + /// + /// The generic parameters on this class. + /// + public IReadOnlyList Generics { get; } + + // TODO: Base class + // TODO: Interfaces? (we might wanna keep them external) + // TODO: Nested classes + + /// + /// The constructors defined on this class. + /// + public IReadOnlyDictionary Constructors { get; } + + // TODO: fields + // TODO: properties + // TODO: methods +} From 6135261e690f2347b6ccaffa04fdbf90f82fd76c Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 13:21:29 +0100 Subject: [PATCH 19/59] Update IClass.cs --- src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs index e2baf87da..6bd1d01a7 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -22,6 +22,11 @@ internal interface IClass /// public string Name { get; } + /// + /// The parent class this class is defined in, if any. + /// + public IClass? DeclaringClass { get; } + /// /// The module this class is defined in. /// From fd81bc15cf9e0209f9b17f58c2fe0231dfcc090b Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 13:28:04 +0100 Subject: [PATCH 20/59] Added a basic, empty impl --- .../Internal/OptimizingIr/Model/Class.cs | 36 +++++++++++++++++++ .../Internal/OptimizingIr/Model/IClass.cs | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs new file mode 100644 index 000000000..07c3a4e42 --- /dev/null +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Internal.Symbols; + +namespace Draco.Compiler.Internal.OptimizingIr.Model; + +internal sealed class Class : IClass +{ + public TypeSymbol Symbol { get; } + + public string Name => this.Symbol.Name; + + public Class? DeclaringClass { get; } + IClass? IClass.DeclaringClass => this.DeclaringClass; + + public Module DeclaringModule { get; } + IModule IClass.DeclaringModule => this.DeclaringModule; + + public Assembly Assembly => this.DeclaringModule.Assembly; + IAssembly IClass.Assembly => this.Assembly; + + public IReadOnlyList Generics => this.Symbol.GenericParameters; + + // TODO + public IReadOnlyDictionary Constructors => throw new NotImplementedException(); + + public Class(Module declaringModule, Class? declaringClass, TypeSymbol symbol) + { + this.DeclaringModule = declaringModule; + this.DeclaringClass = declaringClass; + this.Symbol = symbol; + } +} diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs index 6bd1d01a7..732b6a3a7 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Draco.Compiler.Api.Semantics; +using Draco.Compiler.Internal.Symbols; namespace Draco.Compiler.Internal.OptimizingIr.Model; From 9f10f2989feecee6bd872e59f9af1587e02b98c9 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 14:56:18 +0100 Subject: [PATCH 21/59] Changed ctors to procedures --- .../Internal/OptimizingIr/Model/Class.cs | 5 +++-- .../Internal/OptimizingIr/Model/IClass.cs | 10 ++++------ src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs | 5 +++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index 07c3a4e42..f39233545 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -24,8 +24,9 @@ internal sealed class Class : IClass public IReadOnlyList Generics => this.Symbol.GenericParameters; - // TODO - public IReadOnlyDictionary Constructors => throw new NotImplementedException(); + public IReadOnlyDictionary Procedures => this.procedures; + + private readonly Dictionary procedures = new(); public Class(Module declaringModule, Class? declaringClass, TypeSymbol symbol) { diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs index 732b6a3a7..c780fd19e 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -45,13 +45,11 @@ internal interface IClass // TODO: Base class // TODO: Interfaces? (we might wanna keep them external) // TODO: Nested classes + // TODO: fields + // TODO: properties /// - /// The constructors defined on this class. + /// The procedures defined on this class. /// - public IReadOnlyDictionary Constructors { get; } - - // TODO: fields - // TODO: properties - // TODO: methods + public IReadOnlyDictionary Procedures { get; } } diff --git a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs index a44f93ab2..55730803c 100644 --- a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs @@ -83,6 +83,11 @@ internal abstract partial class FunctionSymbol : Symbol, ITypedSymbol, IMemberSy /// public bool IsVariadic => this.Parameters.Length > 0 && this.Parameters[^1].IsVariadic; + /// + /// True, if this is a constructor. + /// + public bool IsConstructor => this.Name == ".ctor"; + public override FunctionSymbol? GenericDefinition => null; public override Api.Semantics.Visibility Visibility From 7dbd86318b4a4e18adadc60713f108582bc87560 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 17:35:31 +0100 Subject: [PATCH 22/59] Update DefaultConstructorSymbol.cs --- .../Symbols/Synthetized/DefaultConstructorSymbol.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 2fa33e64f..9203cdd3d 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -4,26 +4,24 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Draco.Compiler.Internal.BoundTree; namespace Draco.Compiler.Internal.Symbols.Synthetized; /// /// A default constructor for in-source types. /// -internal sealed class DefaultConstructorSymbol : IrFunctionSymbol +internal sealed class DefaultConstructorSymbol : SynthetizedFunctionSymbol { public override Symbol ContainingSymbol { get; } - public override CodegenDelegate Codegen => (codegen, target, args) => - { - // TODO - throw new NotImplementedException(); - }; - + public override string Name => ".ctor"; public override ImmutableArray Parameters => ImmutableArray.Empty; public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; public override bool IsStatic => false; + public override BoundStatement Body => throw new NotImplementedException(); + public DefaultConstructorSymbol(Symbol containingSymbol) { this.ContainingSymbol = containingSymbol; From 3f4239bffce9554f4c574cf99261e945cb2e720e Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 17:44:27 +0100 Subject: [PATCH 23/59] Added to the codegen flow --- .../Internal/OptimizingIr/ClassCodegen.cs | 40 +++++++++++++++++++ .../Internal/OptimizingIr/ModuleCodegen.cs | 34 +++++++++++----- 2 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs new file mode 100644 index 000000000..5614a42f1 --- /dev/null +++ b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api; +using Draco.Compiler.Internal.OptimizingIr.Model; +using Draco.Compiler.Internal.Symbols; + +namespace Draco.Compiler.Internal.OptimizingIr; + +/// +/// Generates IR code for a class. +/// +internal sealed class ClassCodegen : SymbolVisitor +{ + private Compilation Compilation => this.moduleCodegen.Compilation; + private bool EmitSequencePoints => this.moduleCodegen.EmitSequencePoints; + + private readonly ModuleCodegen moduleCodegen; + private readonly Class @class; + + public ClassCodegen(ModuleCodegen moduleCodegen, Class @class) + { + this.moduleCodegen = moduleCodegen; + this.@class = @class; + } + + public override void VisitFunction(FunctionSymbol functionSymbol) + { + // TODO + throw new NotImplementedException(); + } + + public override void VisitField(FieldSymbol fieldSymbol) + { + // TODO + throw new NotImplementedException(); + } +} diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs index e9b824545..7ede4a71d 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs @@ -14,17 +14,25 @@ namespace Draco.Compiler.Internal.OptimizingIr; /// internal sealed class ModuleCodegen : SymbolVisitor { + /// + /// The compilation the codegen generates for. + /// + public Compilation Compilation { get; } + + /// + /// True, if sequence points should be emitted. + /// + public bool EmitSequencePoints { get; } + private readonly FunctionBodyCodegen globalInitializer; - private readonly Compilation compilation; private readonly Module module; - private readonly bool emitSequencePoints; public ModuleCodegen(Compilation compilation, Module module, bool emitSequencePoints) { - this.compilation = compilation; + this.Compilation = compilation; this.module = module; - this.globalInitializer = new(this.compilation, module.GlobalInitializer); - this.emitSequencePoints = emitSequencePoints; + this.globalInitializer = new(this.Compilation, module.GlobalInitializer); + this.EmitSequencePoints = emitSequencePoints; } private void Complete() @@ -34,6 +42,14 @@ private void Complete() this.globalInitializer.Write(Ret(default(Void))); } + public override void VisitType(TypeSymbol typeSymbol) + { + if (typeSymbol is not SourceClassSymbol sourceClass) return; + + // TODO: Add class + throw new System.NotImplementedException(); + } + public override void VisitGlobal(GlobalSymbol globalSymbol) { if (globalSymbol is not SourceGlobalSymbol sourceGlobal) return; @@ -69,7 +85,7 @@ public override void VisitFunction(FunctionSymbol functionSymbol) // Yank out potential local functions and closures var (bodyWithoutLocalFunctions, localFunctions) = ClosureRewriter.Rewrite(body); // Compile it - var bodyCodegen = new FunctionBodyCodegen(this.compilation, procedure); + var bodyCodegen = new FunctionBodyCodegen(this.Compilation, procedure); bodyWithoutLocalFunctions.Accept(bodyCodegen); // Compile the local functions @@ -81,7 +97,7 @@ public override void VisitModule(ModuleSymbol moduleSymbol) foreach (var subModuleSymbol in moduleSymbol.Members.OfType()) { var module = this.module.DefineModule(subModuleSymbol); - var moduleCodegen = new ModuleCodegen(this.compilation, module, this.emitSequencePoints); + var moduleCodegen = new ModuleCodegen(this.Compilation, module, this.EmitSequencePoints); subModuleSymbol.Accept(moduleCodegen); } @@ -95,8 +111,8 @@ public override void VisitModule(ModuleSymbol moduleSymbol) private BoundNode RewriteBody(BoundNode body) { // If needed, inject sequence points - if (this.emitSequencePoints) body = SequencePointInjector.Inject(body); + if (this.EmitSequencePoints) body = SequencePointInjector.Inject(body); // Desugar it - return body.Accept(new LocalRewriter(this.compilation)); + return body.Accept(new LocalRewriter(this.Compilation)); } } From 45701be6d349c75164be04a81f13b25687e1c5ef Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 3 Dec 2023 17:57:11 +0100 Subject: [PATCH 24/59] Added class to module --- .../Internal/OptimizingIr/Model/Class.cs | 16 ++++++++++++++++ .../Internal/OptimizingIr/Model/IModule.cs | 5 +++++ .../Internal/OptimizingIr/Model/Module.cs | 15 +++++++++++++++ .../Internal/OptimizingIr/ModuleCodegen.cs | 8 ++++++-- .../Internal/Symbols/Source/SourceClassSymbol.cs | 2 +- 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index f39233545..61db35f19 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -34,4 +34,20 @@ public Class(Module declaringModule, Class? declaringClass, TypeSymbol symbol) this.DeclaringClass = declaringClass; this.Symbol = symbol; } + + public override string ToString() + { + var result = new StringBuilder(); + + // TODO: Modifiers + + result.AppendLine($"class {this.Name}"); + result.AppendLine("{"); + + // TODO: add members + + result.AppendLine("}"); + + return result.ToString(); + } } diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IModule.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IModule.cs index fe07e6f71..507429490 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IModule.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IModule.cs @@ -33,6 +33,11 @@ internal interface IModule /// public IReadOnlyDictionary Submodules { get; } + /// + /// The compiled classes within this module. + /// + public IReadOnlyDictionary Classes { get; } + /// /// The globals within this module. /// diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs index 97b1f941b..4a3dccb19 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs @@ -17,6 +17,7 @@ internal sealed class Module : IModule public string Name => this.Symbol.Name; public IReadOnlyDictionary Submodules => this.submodules; + public IReadOnlyDictionary Classes => this.classes; public IReadOnlySet Globals => this.globals; @@ -32,6 +33,7 @@ internal sealed class Module : IModule IModule? IModule.Parent => this.Parent; private readonly HashSet globals = new(); + private readonly Dictionary classes = new(); private readonly Dictionary procedures = new(); private readonly Dictionary submodules = new(); @@ -69,6 +71,16 @@ public Procedure DefineProcedure(FunctionSymbol functionSymbol) return (Procedure)result; } + public Class DefineClass(TypeSymbol typeSymbol) + { + if (!this.classes.TryGetValue(typeSymbol, out var result)) + { + result = new Class(this, null, typeSymbol); + this.classes.Add(typeSymbol, result); + } + return (Class)result; + } + public Module DefineModule(ModuleSymbol moduleSymbol) { if (!this.submodules.TryGetValue(moduleSymbol, out var result)) @@ -83,11 +95,14 @@ public override string ToString() { var result = new StringBuilder(); result.AppendLine($"module {this.Symbol.Name}"); + result.AppendLine("{"); result.AppendJoin(Environment.NewLine, this.globals); + // TODO: Also print classes if (this.globals.Count > 0 && this.procedures.Count > 1) result.Append(doubleNewline); result.AppendJoin(doubleNewline, this.procedures.Values); if (this.procedures.Count > 0 && this.submodules.Count > 0) result.Append(doubleNewline); result.AppendJoin(doubleNewline, this.submodules.Values); + result.AppendLine("}"); return result.ToString(); } } diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs index 7ede4a71d..cffba6edb 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs @@ -46,8 +46,12 @@ public override void VisitType(TypeSymbol typeSymbol) { if (typeSymbol is not SourceClassSymbol sourceClass) return; - // TODO: Add class - throw new System.NotImplementedException(); + // Add it to the module + var @class = this.module.DefineClass(sourceClass); + + // Invoke codegen + var classCodegen = new ClassCodegen(this, @class); + sourceClass.Accept(classCodegen); } public override void VisitGlobal(GlobalSymbol globalSymbol) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 1c2548d86..33a6d71e3 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -66,7 +66,7 @@ private ImmutableArray BuildMembers() // If there is no constructor, add a default one if (this.DeclaringSyntax.PrimaryConstructor is null) { - result.Add(new DefaultConstructorSymbol(this)); + // result.Add(new DefaultConstructorSymbol(this)); } // Done From 769fb32daa6c2015a682bb5450da37d125d9a29a Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Mon, 4 Dec 2023 10:10:15 +0100 Subject: [PATCH 25/59] Fixed ToString --- .../Internal/OptimizingIr/Model/Class.cs | 5 ++-- .../Internal/OptimizingIr/Model/Module.cs | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index 61db35f19..64cf5f3ed 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -41,12 +41,11 @@ public override string ToString() // TODO: Modifiers - result.AppendLine($"class {this.Name}"); - result.AppendLine("{"); + result.AppendLine($"class {this.Name} {{"); // TODO: add members - result.AppendLine("}"); + result.Append('}'); return result.ToString(); } diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs index 4a3dccb19..67364ab51 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs @@ -94,15 +94,24 @@ public Module DefineModule(ModuleSymbol moduleSymbol) public override string ToString() { var result = new StringBuilder(); - result.AppendLine($"module {this.Symbol.Name}"); - result.AppendLine("{"); + result.AppendLine($"module {this.Symbol.Name} {{"); result.AppendJoin(Environment.NewLine, this.globals); - // TODO: Also print classes - if (this.globals.Count > 0 && this.procedures.Count > 1) result.Append(doubleNewline); - result.AppendJoin(doubleNewline, this.procedures.Values); - if (this.procedures.Count > 0 && this.submodules.Count > 0) result.Append(doubleNewline); - result.AppendJoin(doubleNewline, this.submodules.Values); - result.AppendLine("}"); + + var haveNewline = this.globals.Count == 0; + void PrintComponents(IEnumerable components) + { + if (!components.Any()) return; + if (!haveNewline) result!.Append(doubleNewline); + result!.AppendJoin(doubleNewline, components); + haveNewline = false; + } + + PrintComponents(this.procedures.Values); + PrintComponents(this.classes.Values); + PrintComponents(this.submodules.Values); + + if (!haveNewline) result.AppendLine(); + result.Append('}'); return result.ToString(); } } From 04eea0a762db98b27b8172e6d3a72a30d116f1f2 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Mon, 4 Dec 2023 10:40:25 +0100 Subject: [PATCH 26/59] Update MetadataCodegen.cs --- .../Internal/Codegen/MetadataCodegen.cs | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index a0e05277a..66507a882 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -7,6 +7,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; +using System.Xml.Linq; using Draco.Compiler.Api; using Draco.Compiler.Internal.OptimizingIr.Model; using Draco.Compiler.Internal.Symbols; @@ -380,15 +381,27 @@ private void EncodeAssembly() } } - private void EncodeModule(IModule module, TypeDefinitionHandle? parentModule = null, int fieldIndex = 1, int procIndex = 1) + private void EncodeModule(IModule module) { - var currentFieldIndex = fieldIndex; - var currentProcIndex = procIndex; + var fieldIndex = 1; + var procIndex = 1; + this.EncodeModule(module, parent: null, fieldIndex: ref fieldIndex, procIndex: ref procIndex); + } + + private void EncodeModule( + IModule module, + TypeDefinitionHandle? parent, + ref int fieldIndex, + ref int procIndex) + { + var startFieldIndex = fieldIndex; + var startProcIndex = procIndex; + // Go through globals foreach (var global in module.Globals) { this.EncodeGlobal(global); - currentFieldIndex++; + ++fieldIndex; } // Go through procedures @@ -402,22 +415,17 @@ private void EncodeModule(IModule module, TypeDefinitionHandle? parentModule = n // If this is the entry point, save it if (ReferenceEquals(this.assembly.EntryPoint, procedure)) this.EntryPointHandle = handle; - currentProcIndex++; + + ++procIndex; } // Compile global initializer too this.EncodeProcedure(module.GlobalInitializer, specialName: ".cctor"); - currentProcIndex++; + ++procIndex; - TypeAttributes visibility; - if (module.Symbol.Visibility == Api.Semantics.Visibility.Public) - { - visibility = parentModule is not null ? TypeAttributes.NestedPublic : TypeAttributes.Public; - } - else - { - visibility = parentModule is not null ? TypeAttributes.NestedAssembly : TypeAttributes.NotPublic; - } + var visibility = module.Symbol.Visibility == Api.Semantics.Visibility.Public + ? (parent is not null ? TypeAttributes.NestedPublic : TypeAttributes.Public) + : (parent is not null ? TypeAttributes.NestedAssembly : TypeAttributes.NotPublic); var attributes = visibility | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.BeforeFieldInit | TypeAttributes.Abstract | TypeAttributes.Sealed; var name = string.IsNullOrEmpty(module.Name) ? CompilerConstants.DefaultModuleName : module.Name; @@ -428,19 +436,56 @@ private void EncodeModule(IModule module, TypeDefinitionHandle? parentModule = n @namespace: default, name: name, baseType: this.systemObjectReference, - fieldList: MetadataTokens.FieldDefinitionHandle(fieldIndex), - methodList: MetadataTokens.MethodDefinitionHandle(procIndex)); + fieldList: MetadataTokens.FieldDefinitionHandle(startFieldIndex), + methodList: MetadataTokens.MethodDefinitionHandle(startProcIndex)); // If this isn't top level module, we specify nested relationship - if (parentModule is not null) this.MetadataBuilder.AddNestedType(createdModule, parentModule.Value); + if (parent is not null) this.MetadataBuilder.AddNestedType(createdModule, parent.Value); + + // We encode every class + foreach (var @class in module.Classes.Values) + { + this.EncodeClass(@class, parent: createdModule, fieldIndex: ref fieldIndex, procIndex: ref procIndex); + } // We encode every submodule foreach (var subModule in module.Submodules.Values) { - this.EncodeModule(subModule, createdModule, currentFieldIndex, currentProcIndex); + this.EncodeModule(subModule, parent: createdModule, fieldIndex: ref fieldIndex, procIndex: ref procIndex); } } + private TypeDefinitionHandle EncodeClass( + IClass @class, + TypeDefinitionHandle? parent, + ref int fieldIndex, + ref int procIndex) + { + var startFieldIndex = fieldIndex; + var startProcIndex = procIndex; + + // TODO: Go through members + + var visibility = @class.Symbol.Visibility == Api.Semantics.Visibility.Public + ? (parent is not null ? TypeAttributes.NestedPublic : TypeAttributes.Public) + : (parent is not null ? TypeAttributes.NestedAssembly : TypeAttributes.NotPublic); + var attributes = visibility | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.BeforeFieldInit | TypeAttributes.Sealed; + + // Create the type + var createdClass = this.AddTypeDefinition( + attributes: attributes, + @namespace: default, + name: @class.Name, + baseType: this.systemObjectReference, + fieldList: MetadataTokens.FieldDefinitionHandle(startFieldIndex), + methodList: MetadataTokens.MethodDefinitionHandle(startProcIndex)); + + // If this isn't top level module, we specify nested relationship + if (parent is not null) this.MetadataBuilder.AddNestedType(createdClass, parent.Value); + + return createdClass; + } + private FieldDefinitionHandle EncodeGlobal(GlobalSymbol global) { var visibility = global.Visibility switch From 88364287af0c5e4c1c452a9a4eae0f9256722900 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 20:47:45 +0100 Subject: [PATCH 27/59] Fix --- src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs | 2 +- .../Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 33a6d71e3..1c2548d86 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -66,7 +66,7 @@ private ImmutableArray BuildMembers() // If there is no constructor, add a default one if (this.DeclaringSyntax.PrimaryConstructor is null) { - // result.Add(new DefaultConstructorSymbol(this)); + result.Add(new DefaultConstructorSymbol(this)); } // Done diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 9203cdd3d..21064a4ec 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -11,7 +11,7 @@ namespace Draco.Compiler.Internal.Symbols.Synthetized; /// /// A default constructor for in-source types. /// -internal sealed class DefaultConstructorSymbol : SynthetizedFunctionSymbol +internal sealed class DefaultConstructorSymbol : FunctionSymbol { public override Symbol ContainingSymbol { get; } From 3bb41d0b3df6155882b40ace06f282f7b529571a Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 21:05:04 +0100 Subject: [PATCH 28/59] Added base call --- .../Synthetized/DefaultConstructorSymbol.cs | 35 ++++++++++++++++--- .../SynthetizedThisParameterSymbol.cs | 22 ++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 21064a4ec..2cd143068 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Draco.Compiler.Internal.BoundTree; +using static Draco.Compiler.Internal.BoundTree.BoundTreeFactory; namespace Draco.Compiler.Internal.Symbols.Synthetized; @@ -13,17 +14,43 @@ namespace Draco.Compiler.Internal.Symbols.Synthetized; /// internal sealed class DefaultConstructorSymbol : FunctionSymbol { - public override Symbol ContainingSymbol { get; } + public override TypeSymbol ContainingSymbol { get; } public override string Name => ".ctor"; - public override ImmutableArray Parameters => ImmutableArray.Empty; public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; public override bool IsStatic => false; - public override BoundStatement Body => throw new NotImplementedException(); + public override ImmutableArray Parameters => + InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); + private ImmutableArray parameters; - public DefaultConstructorSymbol(Symbol containingSymbol) + public override BoundStatement Body => this.body ??= this.BuildBody(); + private BoundStatement? body; + + public DefaultConstructorSymbol(TypeSymbol containingSymbol) { this.ContainingSymbol = containingSymbol; } + + private ImmutableArray BuildParameters() => + ImmutableArray.Create(new SynthetizedThisParameterSymbol(this)); + + private BoundStatement BuildBody() + { + if (this.ContainingSymbol.BaseType is null) + { + // No base type, no need to call base constructor + return ExpressionStatement(ReturnExpression(UnitExpression())); + } + + // We have a base type, call base constructor + var defaultCtor = this.ContainingSymbol.BaseType.Constructors + .FirstOrDefault(ctor => ctor.Parameters.Length == 1); + // TODO: Error if base has no default constructor? + if (defaultCtor is null) throw new NotImplementedException(); + return ExpressionStatement(CallExpression( + receiver: ParameterExpression(this.Parameters[0]), + method: defaultCtor, + arguments: ImmutableArray.Empty)); + } } diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs new file mode 100644 index 000000000..5e67d18f5 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Draco.Compiler.Internal.Symbols.Synthetized; + +/// +/// Represents the "this" parameter of a function generated by the compiler. +/// +internal sealed class SynthetizedThisParameterSymbol : ParameterSymbol +{ + public override FunctionSymbol ContainingSymbol { get; } + public override TypeSymbol Type => (TypeSymbol)this.ContainingSymbol.ContainingSymbol!; + public override string Name => "this"; + + public SynthetizedThisParameterSymbol(FunctionSymbol containingSymbol) + { + this.ContainingSymbol = containingSymbol; + } +} From 213a4986b45909b0a1dbcfc2eb36028311360986 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 21:13:51 +0100 Subject: [PATCH 29/59] Update ModuleCodegen.cs --- src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs index cffba6edb..548e879f9 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ModuleCodegen.cs @@ -79,13 +79,13 @@ public override void VisitGlobal(GlobalSymbol globalSymbol) public override void VisitFunction(FunctionSymbol functionSymbol) { - if (functionSymbol is not SourceFunctionSymbol sourceFunction) return; + if (functionSymbol.Body is null) return; // Add procedure var procedure = this.module.DefineProcedure(functionSymbol); // Create the body - var body = this.RewriteBody(sourceFunction.Body); + var body = this.RewriteBody(functionSymbol.Body); // Yank out potential local functions and closures var (bodyWithoutLocalFunctions, localFunctions) = ClosureRewriter.Rewrite(body); // Compile it From 32fdbd6b498ccb65632a5dccb9a120125892e617 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 21:32:21 +0100 Subject: [PATCH 30/59] Generating default ctor in IL --- .../Internal/Codegen/MetadataCodegen.cs | 17 ++++++++--- .../Internal/OptimizingIr/ClassCodegen.cs | 29 +++++++++++++++++-- .../OptimizingIr/FunctionBodyCodegen.cs | 3 ++ .../Internal/OptimizingIr/Model/Class.cs | 10 +++++++ .../Internal/OptimizingIr/Model/IProcedure.cs | 1 + .../Internal/OptimizingIr/Model/Procedure.cs | 1 - .../Internal/Symbols/ParameterSymbol.cs | 5 ++++ .../SynthetizedThisParameterSymbol.cs | 1 + 8 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index eaa828783..a3d3af04c 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -464,7 +464,9 @@ private TypeDefinitionHandle EncodeClass( var startFieldIndex = fieldIndex; var startProcIndex = procIndex; - // TODO: Go through members + foreach (var proc in @class.Procedures.Values) this.EncodeProcedure(proc); + + // TODO: Go through the rest of the members var visibility = @class.Symbol.Visibility == Api.Semantics.Visibility.Public ? (parent is not null ? TypeAttributes.NestedPublic : TypeAttributes.Public) @@ -535,7 +537,8 @@ private MethodDefinitionHandle EncodeProcedure(IProcedure procedure, string? spe hasDynamicStackAllocation: false); // Determine attributes - var attributes = MethodAttributes.Static | MethodAttributes.HideBySig; + var attributes = MethodAttributes.HideBySig; + if (procedure.Symbol.IsStatic) attributes |= MethodAttributes.Static; attributes |= specialName is null ? visibility : MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; @@ -582,11 +585,17 @@ private BlobHandle EncodeGlobalSignature(GlobalSymbol global) => private BlobHandle EncodeProcedureSignature(IProcedure procedure) => this.EncodeBlob(e => { e - .MethodSignature(genericParameterCount: procedure.Generics.Count) - .Parameters(procedure.Parameters.Count, out var retEncoder, out var paramsEncoder); + .MethodSignature( + genericParameterCount: procedure.Generics.Count, + isInstanceMethod: !procedure.Symbol.IsStatic) + .Parameters( + procedure.Symbol.IsStatic ? procedure.Parameters.Count : procedure.Parameters.Count - 1, + out var retEncoder, + out var paramsEncoder); this.EncodeReturnType(retEncoder, procedure.ReturnType); foreach (var param in procedure.Parameters) { + if (param.IsThis) continue; this.EncodeSignatureType(paramsEncoder.AddParameter().Type(), param.Type); } }); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs index 5614a42f1..e6edf14c2 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs @@ -4,6 +4,8 @@ using System.Text; using System.Threading.Tasks; using Draco.Compiler.Api; +using Draco.Compiler.Internal.BoundTree; +using Draco.Compiler.Internal.Lowering; using Draco.Compiler.Internal.OptimizingIr.Model; using Draco.Compiler.Internal.Symbols; @@ -26,10 +28,24 @@ public ClassCodegen(ModuleCodegen moduleCodegen, Class @class) this.@class = @class; } + // TODO: Copypasta from ModuleCodegen public override void VisitFunction(FunctionSymbol functionSymbol) { - // TODO - throw new NotImplementedException(); + if (functionSymbol.Body is null) return; + + // Add procedure + var procedure = this.@class.DefineProcedure(functionSymbol); + + // Create the body + var body = this.RewriteBody(functionSymbol.Body); + // Yank out potential local functions and closures + var (bodyWithoutLocalFunctions, localFunctions) = ClosureRewriter.Rewrite(body); + // Compile it + var bodyCodegen = new FunctionBodyCodegen(this.Compilation, procedure); + bodyWithoutLocalFunctions.Accept(bodyCodegen); + + // Compile the local functions + foreach (var localFunc in localFunctions) this.VisitFunction(localFunc); } public override void VisitField(FieldSymbol fieldSymbol) @@ -37,4 +53,13 @@ public override void VisitField(FieldSymbol fieldSymbol) // TODO throw new NotImplementedException(); } + + // TODO: Copypasta from ModuleCodegen + private BoundNode RewriteBody(BoundNode body) + { + // If needed, inject sequence points + if (this.EmitSequencePoints) body = SequencePointInjector.Inject(body); + // Desugar it + return body.Accept(new LocalRewriter(this.Compilation)); + } } diff --git a/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs index 4c7435c5f..1b6a91e63 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs @@ -74,6 +74,9 @@ private Procedure SynthetizeProcedure(FunctionSymbol func) { Debug.Assert(func.Body is not null); + // TODO: This might not be necessarily correct, as we might be within a class + // and not a module + // We handle synthetized functions a bit specially, as they are not part of our symbol // tree, so we compile them, in case they have not yet been var compiledAlready = this.procedure.DeclaringModule.Procedures.ContainsKey(func); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index 64cf5f3ed..6c9d8eab8 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -35,6 +35,16 @@ public Class(Module declaringModule, Class? declaringClass, TypeSymbol symbol) this.Symbol = symbol; } + public Procedure DefineProcedure(FunctionSymbol functionSymbol) + { + if (!this.procedures.TryGetValue(functionSymbol, out var result)) + { + result = new Procedure(this.DeclaringModule, functionSymbol); + this.procedures.Add(functionSymbol, result); + } + return (Procedure)result; + } + public override string ToString() { var result = new StringBuilder(); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IProcedure.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IProcedure.cs index 9e263f768..c06e2b031 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IProcedure.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IProcedure.cs @@ -18,6 +18,7 @@ internal interface IProcedure /// public string Name { get; } + // TODO: Declaring class? /// /// The module this procedure is defined in. /// diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Procedure.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Procedure.cs index 05bc8ebb6..a1962f9fe 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Procedure.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Procedure.cs @@ -13,7 +13,6 @@ internal sealed class Procedure : IProcedure { public FunctionSymbol Symbol { get; } public string Name => this.Symbol.Name; - public TypeSymbol? Type => this.Symbol.Type; public Module DeclaringModule { get; } IModule IProcedure.DeclaringModule => this.DeclaringModule; public Assembly Assembly => this.DeclaringModule.Assembly; diff --git a/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs index 0b639a5ca..36710ab08 100644 --- a/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs @@ -14,6 +14,11 @@ internal abstract partial class ParameterSymbol : LocalSymbol /// public virtual bool IsVariadic => false; + /// + /// True, if this is the "this" parameter. + /// + public virtual bool IsThis => false; + public override bool IsMutable => false; // NOTE: Override for covariant return type diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs index 5e67d18f5..c1660e574 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs @@ -14,6 +14,7 @@ internal sealed class SynthetizedThisParameterSymbol : ParameterSymbol public override FunctionSymbol ContainingSymbol { get; } public override TypeSymbol Type => (TypeSymbol)this.ContainingSymbol.ContainingSymbol!; public override string Name => "this"; + public override bool IsThis => true; public SynthetizedThisParameterSymbol(FunctionSymbol containingSymbol) { From 5dde63644b5786b79cb91db53fcf43c5f43dc2f1 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 21:39:06 +0100 Subject: [PATCH 31/59] Generating default ctor --- .../Internal/Symbols/Source/SourceClassSymbol.cs | 13 +++++++++++++ .../Synthetized/DefaultConstructorSymbol.cs | 15 ++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 1c2548d86..eef47ed62 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -28,6 +28,10 @@ internal sealed class SourceClassSymbol : TypeSymbol InterlockedUtils.InitializeDefault(ref this.genericParameters, this.BuildGenericParameters); private ImmutableArray genericParameters; + public override ImmutableArray ImmediateBaseTypes => + InterlockedUtils.InitializeDefault(ref this.immediateBaseTypes, this.BuildImmediateBaseTypes); + private ImmutableArray immediateBaseTypes; + public override bool IsValueType => this.DeclaringSyntax.ValueModifier is not null; public override Symbol ContainingSymbol { get; } @@ -58,6 +62,15 @@ private ImmutableArray BuildGenericParameters() .ToImmutableArray(); } + private ImmutableArray BuildImmediateBaseTypes() + { + var result = ImmutableArray.CreateBuilder(); + // NOTE: For now we always just inherit from object + result.Add(this.DeclaringCompilation!.WellKnownTypes.SystemObject); + // Done + return result.ToImmutable(); + } + private ImmutableArray BuildMembers() { var result = ImmutableArray.CreateBuilder(); diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 2cd143068..640380496 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -45,12 +45,17 @@ private BoundStatement BuildBody() // We have a base type, call base constructor var defaultCtor = this.ContainingSymbol.BaseType.Constructors - .FirstOrDefault(ctor => ctor.Parameters.Length == 1); + .FirstOrDefault(ctor => ctor.Parameters.Length == 0); // TODO: Error if base has no default constructor? if (defaultCtor is null) throw new NotImplementedException(); - return ExpressionStatement(CallExpression( - receiver: ParameterExpression(this.Parameters[0]), - method: defaultCtor, - arguments: ImmutableArray.Empty)); + return ExpressionStatement(BlockExpression( + locals: ImmutableArray.Empty, + statements: ImmutableArray.Create( + ExpressionStatement(CallExpression( + receiver: ParameterExpression(this.Parameters[0]), + method: defaultCtor, + arguments: ImmutableArray.Empty)), + ExpressionStatement(ReturnExpression(BoundUnitExpression.Default))), + value: BoundUnitExpression.Default)); } } From 44468e85818907e887d9ab0bf027824defb453d4 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 16 Dec 2023 21:41:10 +0100 Subject: [PATCH 32/59] Update DefaultConstructorSymbol.cs --- .../Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 640380496..9aacb0f9b 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -43,6 +43,10 @@ private BoundStatement BuildBody() return ExpressionStatement(ReturnExpression(UnitExpression())); } + // TODO: Filtering for 0 args is not correct + // while it is fine for metadata functions, for source functions "this" is explicit so it has 1 arg + // We either kill this asimmetry, or we never make assumptions about the number of args + // We have a base type, call base constructor var defaultCtor = this.ContainingSymbol.BaseType.Constructors .FirstOrDefault(ctor => ctor.Parameters.Length == 0); From 8d9bda344a9648408eb8b88dd68629fb024dfab8 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 17 Dec 2023 10:10:28 +0100 Subject: [PATCH 33/59] Update ClassCodegen.cs --- src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs index e6edf14c2..1cadbf450 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs @@ -55,10 +55,11 @@ public override void VisitField(FieldSymbol fieldSymbol) } // TODO: Copypasta from ModuleCodegen + // TODO: Except we check for syntax not being null because we can have synthetized symbols private BoundNode RewriteBody(BoundNode body) { // If needed, inject sequence points - if (this.EmitSequencePoints) body = SequencePointInjector.Inject(body); + if (body.Syntax is not null && this.EmitSequencePoints) body = SequencePointInjector.Inject(body); // Desugar it return body.Accept(new LocalRewriter(this.Compilation)); } From 21f57dc4c700634f2e20ebe54585e22fffe240d5 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 17 Dec 2023 11:20:25 +0100 Subject: [PATCH 34/59] Added all required attribs --- .../Internal/Codegen/MetadataCodegen.cs | 12 +++++------- .../Internal/OptimizingIr/Model/Module.cs | 2 +- .../Symbols/Synthetized/DefaultConstructorSymbol.cs | 1 + .../Symbols/Synthetized/IntrinsicFunctionSymbol.cs | 1 + 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index a3d3af04c..8bfd0d163 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -420,7 +420,7 @@ private void EncodeModule( } // Compile global initializer too - this.EncodeProcedure(module.GlobalInitializer, specialName: ".cctor"); + this.EncodeProcedure(module.GlobalInitializer); ++procIndex; var visibility = module.Symbol.Visibility == Api.Semantics.Visibility.Public @@ -505,7 +505,7 @@ private FieldDefinitionHandle EncodeGlobal(GlobalSymbol global) signature: this.EncodeGlobalSignature(global)); } - private MethodDefinitionHandle EncodeProcedure(IProcedure procedure, string? specialName = null) + private MethodDefinitionHandle EncodeProcedure(IProcedure procedure) { var visibility = procedure.Symbol.Visibility switch { @@ -537,11 +537,9 @@ private MethodDefinitionHandle EncodeProcedure(IProcedure procedure, string? spe hasDynamicStackAllocation: false); // Determine attributes - var attributes = MethodAttributes.HideBySig; + var attributes = MethodAttributes.HideBySig | visibility; if (procedure.Symbol.IsStatic) attributes |= MethodAttributes.Static; - attributes |= specialName is null - ? visibility - : MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + if (procedure.Symbol.IsSpecialName) attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; // Parameters var parameterList = this.NextParameterHandle; @@ -557,7 +555,7 @@ private MethodDefinitionHandle EncodeProcedure(IProcedure procedure, string? spe var definitionHandle = this.MetadataBuilder.AddMethodDefinition( attributes: attributes, implAttributes: MethodImplAttributes.IL, - name: this.GetOrAddString(specialName ?? procedure.Name), + name: this.GetOrAddString(procedure.Name), signature: this.EncodeProcedureSignature(procedure), bodyOffset: methodBodyOffset, parameterList: parameterList); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs index 67364ab51..77e1ab9bf 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Module.cs @@ -41,7 +41,7 @@ public Module(ModuleSymbol symbol, Assembly assembly, Module? Parent) { this.Symbol = symbol; this.GlobalInitializer = this.DefineProcedure(new IntrinsicFunctionSymbol( - name: "", + name: ".cctor", paramTypes: Enumerable.Empty(), returnType: IntrinsicSymbols.Unit)); this.Assembly = assembly; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 9aacb0f9b..3cd20528c 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -19,6 +19,7 @@ internal sealed class DefaultConstructorSymbol : FunctionSymbol public override string Name => ".ctor"; public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; public override bool IsStatic => false; + public override bool IsSpecialName => true; public override ImmutableArray Parameters => InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/IntrinsicFunctionSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/IntrinsicFunctionSymbol.cs index ac16ebcdb..8d2a4e117 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/IntrinsicFunctionSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/IntrinsicFunctionSymbol.cs @@ -13,6 +13,7 @@ internal sealed class IntrinsicFunctionSymbol : FunctionSymbol public override TypeSymbol ReturnType { get; } public override bool IsSpecialName => true; + public override Api.Semantics.Visibility Visibility => Api.Semantics.Visibility.Private; public override string Name { get; } From a72eeadc78a81893482bb5df7ae98a09493f798c Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 17 Dec 2023 11:22:27 +0100 Subject: [PATCH 35/59] Update DefaultConstructorSymbol.cs --- .../Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 3cd20528c..0d406ac4c 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -20,6 +20,7 @@ internal sealed class DefaultConstructorSymbol : FunctionSymbol public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; public override bool IsStatic => false; public override bool IsSpecialName => true; + public override Api.Semantics.Visibility Visibility => Api.Semantics.Visibility.Public; public override ImmutableArray Parameters => InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); From 47bf4f4e35afa0a3c33880aac8e8e0f9c4e00064 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 17 Dec 2023 17:54:21 +0100 Subject: [PATCH 36/59] Can almost invoke ctor function --- .../Symbols/Source/SourceModuleSymbol.cs | 58 ++++++++++++------- .../Synthetized/ConstructorFunctionSymbol.cs | 1 + .../Synthetized/DefaultConstructorSymbol.cs | 1 + .../Internal/Symbols/TypeSymbol.cs | 2 +- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs index dcf8e8d34..75c33e96e 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs @@ -10,6 +10,7 @@ using Draco.Compiler.Internal.Declarations; using Draco.Compiler.Internal.Documentation; using Draco.Compiler.Internal.Documentation.Extractors; +using Draco.Compiler.Internal.Symbols.Synthetized; namespace Draco.Compiler.Internal.Symbols.Source; @@ -72,41 +73,54 @@ private ImmutableArray BindMembers(IBinderProvider binderProvider) // Syntax-declaration foreach (var declaration in this.declaration.Children) { - var member = this.BuildMember(declaration); - var earlierMember = result.FirstOrDefault(s => s.Name == member.Name); - result.Add(member); - - // We chech for illegal shadowing - if (earlierMember is null) continue; - - // Overloading is legal - if (member is FunctionSymbol && earlierMember is FunctionSymbol) continue; - - // Illegal - var syntax = member.DeclaringSyntax; - Debug.Assert(syntax is not null); - binderProvider.DiagnosticBag.Add(Diagnostic.Create( - template: SymbolResolutionErrors.IllegalShadowing, - location: syntax.Location, - formatArgs: member.Name)); + var members = this.BuildMember(declaration); + foreach (var member in members) + { + var earlierMember = result.FirstOrDefault(s => s.Name == member.Name); + result.Add(member); + + // We chech for illegal shadowing + if (earlierMember is null) continue; + if (!earlierMember.CanBeShadowedBy(member)) continue; + + // Illegal + var syntax = member.DeclaringSyntax; + Debug.Assert(syntax is not null); + binderProvider.DiagnosticBag.Add(Diagnostic.Create( + template: SymbolResolutionErrors.IllegalShadowing, + location: syntax.Location, + formatArgs: member.Name)); + } } return result.ToImmutable(); } - private Symbol BuildMember(Declaration declaration) => declaration switch + private IEnumerable BuildMember(Declaration declaration) => declaration switch { - FunctionDeclaration f => this.BuildFunction(f), - GlobalDeclaration g => this.BuildGlobal(g), - MergedModuleDeclaration m => this.BuildModule(m), + FunctionDeclaration f => new[] { this.BuildFunction(f) }, + GlobalDeclaration g => new[] { this.BuildGlobal(g) }, + MergedModuleDeclaration m => new[] { this.BuildModule(m) }, ClassDeclaration c => this.BuildClass(c), _ => throw new ArgumentOutOfRangeException(nameof(declaration)), }; private FunctionSymbol BuildFunction(FunctionDeclaration declaration) => new SourceFunctionSymbol(this, declaration); private GlobalSymbol BuildGlobal(GlobalDeclaration declaration) => new SourceGlobalSymbol(this, declaration); - private TypeSymbol BuildClass(ClassDeclaration declaration) => new SourceClassSymbol(this, declaration); private ModuleSymbol BuildModule(MergedModuleDeclaration declaration) => new SourceModuleSymbol(this.DeclaringCompilation, this, declaration); + private IEnumerable BuildClass(ClassDeclaration declaration) + { + var result = new List(); + var classSymbol = new SourceClassSymbol(this, declaration); + result.Add(classSymbol); + // Add constructor functions + foreach (var ctor in classSymbol.Constructors) + { + var ctorSymbol = new ConstructorFunctionSymbol(ctor); + result.Add(ctorSymbol); + } + return result; + } private SymbolDocumentation BuildDocumentation() => MarkdownDocumentationExtractor.Extract(this); diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ConstructorFunctionSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/ConstructorFunctionSymbol.cs index 6de40356a..8a0d39262 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ConstructorFunctionSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/ConstructorFunctionSymbol.cs @@ -12,6 +12,7 @@ internal sealed class ConstructorFunctionSymbol : FunctionSymbol { public override string Name => this.InstantiatedType.Name; public override bool IsSpecialName => true; + public override Api.Semantics.Visibility Visibility => this.ctorDefinition.Visibility; public override ImmutableArray GenericParameters => InterlockedUtils.InitializeDefault(ref this.genericParameters, this.BuildGenericParameters); diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 0d406ac4c..4eeac08ca 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -20,6 +20,7 @@ internal sealed class DefaultConstructorSymbol : FunctionSymbol public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; public override bool IsStatic => false; public override bool IsSpecialName => true; + public override bool IsConstructor => true; public override Api.Semantics.Visibility Visibility => Api.Semantics.Visibility.Public; public override ImmutableArray Parameters => diff --git a/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs index f550786f7..3ee1570f9 100644 --- a/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/TypeSymbol.cs @@ -102,7 +102,7 @@ public IEnumerable BaseTypes public override bool CanBeShadowedBy(Symbol other) { - if (other is not TypeSymbol type) return false; + if (other is not TypeSymbol) return false; if (this.Name != other.Name) return false; return this.GenericParameters.Length == other.GenericParameters.Length; } From d274496f433468ac97c288a2118d84b667b66d34 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Mon, 18 Dec 2023 13:23:58 +0100 Subject: [PATCH 37/59] Compiles --- src/Draco.Compiler/Internal/Binding/LocalBinder.cs | 2 +- src/Draco.Compiler/Internal/Codegen/CilCodegen.cs | 4 +++- .../Internal/Codegen/MetadataCodegen.cs | 14 ++++++++++++-- .../Internal/Solver/ConstraintSolver_Utils.cs | 3 ++- .../Internal/Symbols/FunctionSymbol.cs | 7 +++++++ .../Symbols/Generic/ParameterInstanceSymbol.cs | 4 ++-- .../Symbols/Metadata/MetadataParameterSymbol.cs | 4 ++-- .../Internal/Symbols/ParameterSymbol.cs | 4 +++- .../Symbols/Source/SourceParameterSymbol.cs | 4 ++-- .../Synthetized/DefaultConstructorSymbol.cs | 13 ++----------- .../Synthetized/SynthetizedParameterSymbol.cs | 6 +++--- 11 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/Draco.Compiler/Internal/Binding/LocalBinder.cs b/src/Draco.Compiler/Internal/Binding/LocalBinder.cs index 9dfde811a..4a67f1849 100644 --- a/src/Draco.Compiler/Internal/Binding/LocalBinder.cs +++ b/src/Draco.Compiler/Internal/Binding/LocalBinder.cs @@ -167,7 +167,7 @@ private void Build() private Symbol? BuildSymbol(SyntaxNode syntax, int localCount) => syntax switch { FunctionDeclarationSyntax function => new SourceFunctionSymbol(this.ContainingSymbol, function), - ParameterSyntax parameter => new SourceParameterSymbol(this.ContainingSymbol, parameter), + ParameterSyntax parameter => new SourceParameterSymbol((FunctionSymbol)this.ContainingSymbol, parameter), VariableDeclarationSyntax variable => new SourceLocalSymbol(this.ContainingSymbol, new TypeVariable(localCount), variable), LabelDeclarationSyntax label => new SourceLabelSymbol(this.ContainingSymbol, label), _ => null, diff --git a/src/Draco.Compiler/Internal/Codegen/CilCodegen.cs b/src/Draco.Compiler/Internal/Codegen/CilCodegen.cs index 16add947b..5391c4b9f 100644 --- a/src/Draco.Compiler/Internal/Codegen/CilCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/CilCodegen.cs @@ -74,7 +74,9 @@ public CilCodegen(MetadataCodegen metadataCodegen, IProcedure procedure) private EntityHandle GetHandle(Symbol symbol) => this.metadataCodegen.GetEntityHandle(symbol); // TODO: Parameters don't handle unit yet, it introduces some signature problems - private int GetParameterIndex(ParameterSymbol parameter) => this.procedure.GetParameterIndex(parameter); + private int GetParameterIndex(ParameterSymbol parameter) => parameter.IsThis + ? 0 + : this.procedure.GetParameterIndex(parameter) + (parameter.ContainingSymbol.IsStatic ? 0 : 1); private AllocatedLocal? GetAllocatedLocal(LocalSymbol local) { diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 8bfd0d163..a9cacb0ad 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -587,13 +587,12 @@ private BlobHandle EncodeProcedureSignature(IProcedure procedure) => this.Encode genericParameterCount: procedure.Generics.Count, isInstanceMethod: !procedure.Symbol.IsStatic) .Parameters( - procedure.Symbol.IsStatic ? procedure.Parameters.Count : procedure.Parameters.Count - 1, + procedure.Parameters.Count, out var retEncoder, out var paramsEncoder); this.EncodeReturnType(retEncoder, procedure.ReturnType); foreach (var param in procedure.Parameters) { - if (param.IsThis) continue; this.EncodeSignatureType(paramsEncoder.AddParameter().Type(), param.Type); } }); @@ -723,6 +722,17 @@ public void EncodeSignatureType(SignatureTypeEncoder encoder, TypeSymbol type) return; } + if (type is SourceClassSymbol sourceClass) + { + encoder.Type( + type: this.GetOrAddTypeReference( + parent: this.GetEntityHandle(sourceClass.ContainingSymbol), + @namespace: null, + name: sourceClass.MetadataName), + isValueType: sourceClass.IsValueType); + return; + } + // TODO throw new NotImplementedException(); } diff --git a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Utils.cs b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Utils.cs index a76c6df41..f0f4b3486 100644 --- a/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Utils.cs +++ b/src/Draco.Compiler/Internal/Solver/ConstraintSolver_Utils.cs @@ -15,7 +15,8 @@ internal sealed partial class ConstraintSolver private FunctionTypeSymbol MakeMismatchedFunctionType(ImmutableArray args, TypeSymbol returnType) => new( args - .Select(a => new SynthetizedParameterSymbol(null, a.Type)) + // TODO: We are passing null here... + .Select(a => new SynthetizedParameterSymbol(null!, a.Type)) .Cast() .ToImmutableArray(), returnType); diff --git a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs index 5f68cd933..56b0b4f28 100644 --- a/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/FunctionSymbol.cs @@ -70,6 +70,13 @@ public delegate void CodegenDelegate( _ => throw new System.ArgumentOutOfRangeException(nameof(token)), }; + /// + /// The receiver of this function, if it has one. + /// + public TypeSymbol? Receiver => this.IsStatic + ? null + : this.ContainingSymbol as TypeSymbol; + /// /// The parameters of this function. /// diff --git a/src/Draco.Compiler/Internal/Symbols/Generic/ParameterInstanceSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Generic/ParameterInstanceSymbol.cs index f1e425b33..a2742e461 100644 --- a/src/Draco.Compiler/Internal/Symbols/Generic/ParameterInstanceSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Generic/ParameterInstanceSymbol.cs @@ -12,12 +12,12 @@ internal sealed class ParameterInstanceSymbol : ParameterSymbol, IGenericInstanc public override bool IsVariadic => this.GenericDefinition.IsVariadic; public override string Name => this.GenericDefinition.Name; - public override Symbol? ContainingSymbol { get; } + public override FunctionSymbol ContainingSymbol { get; } public override ParameterSymbol GenericDefinition { get; } public GenericContext Context { get; } - public ParameterInstanceSymbol(Symbol? containingSymbol, ParameterSymbol genericDefinition, GenericContext context) + public ParameterInstanceSymbol(FunctionSymbol containingSymbol, ParameterSymbol genericDefinition, GenericContext context) { this.ContainingSymbol = containingSymbol; this.GenericDefinition = genericDefinition; diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataParameterSymbol.cs index 75a5ada43..131f8569b 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataParameterSymbol.cs @@ -12,7 +12,7 @@ internal sealed class MetadataParameterSymbol : ParameterSymbol, IMetadataSymbol public override string MetadataName => this.MetadataReader.GetString(this.parameterDefinition.Name); public override TypeSymbol Type { get; } - public override Symbol ContainingSymbol { get; } + public override FunctionSymbol ContainingSymbol { get; } // NOTE: thread-safety does not matter, same instance public MetadataAssemblySymbol Assembly => this.assembly ??= this.AncestorChain.OfType().First(); @@ -22,7 +22,7 @@ internal sealed class MetadataParameterSymbol : ParameterSymbol, IMetadataSymbol private readonly Parameter parameterDefinition; - public MetadataParameterSymbol(Symbol containingSymbol, Parameter parameterDefinition, TypeSymbol type) + public MetadataParameterSymbol(FunctionSymbol containingSymbol, Parameter parameterDefinition, TypeSymbol type) { this.ContainingSymbol = containingSymbol; this.Type = type; diff --git a/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs index 36710ab08..e88c876c3 100644 --- a/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/ParameterSymbol.cs @@ -9,6 +9,8 @@ namespace Draco.Compiler.Internal.Symbols; /// internal abstract partial class ParameterSymbol : LocalSymbol { + public override abstract FunctionSymbol ContainingSymbol { get; } + /// /// True, if this a variadic parameter. /// @@ -27,7 +29,7 @@ internal abstract partial class ParameterSymbol : LocalSymbol public override ParameterSymbol GenericInstantiate(Symbol? containingSymbol, ImmutableArray arguments) => (ParameterSymbol)base.GenericInstantiate(containingSymbol, arguments); public override ParameterSymbol GenericInstantiate(Symbol? containingSymbol, GenericContext context) => - new ParameterInstanceSymbol(containingSymbol, this, context); + new ParameterInstanceSymbol((FunctionSymbol)containingSymbol!, this, context); public override IParameterSymbol ToApiSymbol() => new Api.Semantics.ParameterSymbol(this); diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceParameterSymbol.cs index a4cabdd0f..55568979d 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceParameterSymbol.cs @@ -12,7 +12,7 @@ internal sealed class SourceParameterSymbol : ParameterSymbol, ISourceSymbol public override TypeSymbol Type => this.BindTypeIfNeeded(this.DeclaringCompilation!); private TypeSymbol? type; - public override Symbol ContainingSymbol { get; } + public override FunctionSymbol ContainingSymbol { get; } public override bool IsVariadic => this.DeclaringSyntax.Variadic is not null; public override string Name => this.DeclaringSyntax.Name.Text; @@ -20,7 +20,7 @@ internal sealed class SourceParameterSymbol : ParameterSymbol, ISourceSymbol // TODO: Extracting parameter docs involves looking into the function docs and searching in the MD - public SourceParameterSymbol(Symbol containingSymbol, ParameterSyntax syntax) + public SourceParameterSymbol(FunctionSymbol containingSymbol, ParameterSyntax syntax) { this.ContainingSymbol = containingSymbol; this.DeclaringSyntax = syntax; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 4eeac08ca..28e1e1266 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -23,9 +23,7 @@ internal sealed class DefaultConstructorSymbol : FunctionSymbol public override bool IsConstructor => true; public override Api.Semantics.Visibility Visibility => Api.Semantics.Visibility.Public; - public override ImmutableArray Parameters => - InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); - private ImmutableArray parameters; + public override ImmutableArray Parameters => ImmutableArray.Empty; public override BoundStatement Body => this.body ??= this.BuildBody(); private BoundStatement? body; @@ -35,9 +33,6 @@ public DefaultConstructorSymbol(TypeSymbol containingSymbol) this.ContainingSymbol = containingSymbol; } - private ImmutableArray BuildParameters() => - ImmutableArray.Create(new SynthetizedThisParameterSymbol(this)); - private BoundStatement BuildBody() { if (this.ContainingSymbol.BaseType is null) @@ -46,10 +41,6 @@ private BoundStatement BuildBody() return ExpressionStatement(ReturnExpression(UnitExpression())); } - // TODO: Filtering for 0 args is not correct - // while it is fine for metadata functions, for source functions "this" is explicit so it has 1 arg - // We either kill this asimmetry, or we never make assumptions about the number of args - // We have a base type, call base constructor var defaultCtor = this.ContainingSymbol.BaseType.Constructors .FirstOrDefault(ctor => ctor.Parameters.Length == 0); @@ -59,7 +50,7 @@ private BoundStatement BuildBody() locals: ImmutableArray.Empty, statements: ImmutableArray.Create( ExpressionStatement(CallExpression( - receiver: ParameterExpression(this.Parameters[0]), + receiver: ParameterExpression(new SynthetizedThisParameterSymbol(this)), method: defaultCtor, arguments: ImmutableArray.Empty)), ExpressionStatement(ReturnExpression(BoundUnitExpression.Default))), diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedParameterSymbol.cs index 92daa88ce..b13cb5c98 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedParameterSymbol.cs @@ -8,16 +8,16 @@ internal sealed class SynthetizedParameterSymbol : ParameterSymbol public override string Name { get; } public override TypeSymbol Type { get; } - public override Symbol? ContainingSymbol { get; } + public override FunctionSymbol ContainingSymbol { get; } - public SynthetizedParameterSymbol(Symbol? containingSymbol, string name, TypeSymbol type) + public SynthetizedParameterSymbol(FunctionSymbol containingSymbol, string name, TypeSymbol type) { this.ContainingSymbol = containingSymbol; this.Name = name; this.Type = type; } - public SynthetizedParameterSymbol(Symbol? containingSymbol, TypeSymbol type) + public SynthetizedParameterSymbol(FunctionSymbol containingSymbol, TypeSymbol type) : this(containingSymbol, string.Empty, type) { } From b21586f70db023610d2521cbe0daa5c7c27d7e74 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 11:22:36 +0100 Subject: [PATCH 38/59] Started on primary ctor --- .../Symbols/Source/SourceClassSymbol.cs | 5 ++ .../Source/SourcePrimaryConstructorSymbol.cs | 76 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index eef47ed62..9db2d6a10 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -81,6 +81,11 @@ private ImmutableArray BuildMembers() { result.Add(new DefaultConstructorSymbol(this)); } + else + { + // We have a primary constructor, add it + result.Add(new SourcePrimaryConstructorSymbol(this, this.DeclaringSyntax.PrimaryConstructor)); + } // Done return result.ToImmutable(); diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs new file mode 100644 index 000000000..d3c03e8ed --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api.Diagnostics; +using Draco.Compiler.Api.Syntax; +using Draco.Compiler.Internal.Binding; +using Draco.Compiler.Internal.Symbols.Synthetized; + +namespace Draco.Compiler.Internal.Symbols.Source; + +/// +/// Primary constructor defined in-source. +/// +internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override string Name => ".ctor"; + public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + public override bool IsStatic => false; + public override bool IsSpecialName => true; + public override bool IsConstructor => true; + + public override ImmutableArray Parameters => this.BindParametersIfNeeded(this.DeclaringCompilation!); + private ImmutableArray parameters; + + public override PrimaryConstructorSyntax DeclaringSyntax { get; } + + public SourcePrimaryConstructorSymbol(TypeSymbol containingSymbol, PrimaryConstructorSyntax declaringSyntax) + { + this.ContainingSymbol = containingSymbol; + this.DeclaringSyntax = declaringSyntax; + } + + private ImmutableArray BindParametersIfNeeded(IBinderProvider binderProvider) => + InterlockedUtils.InitializeDefault(ref this.parameters, () => this.BindParameters(binderProvider)); + + // TODO: Copypaste from SourceFunctionSymbol + private ImmutableArray BindParameters(IBinderProvider binderProvider) + { + var parameterSyntaxes = this.DeclaringSyntax.ParameterList.Values.ToList(); + var parameters = ImmutableArray.CreateBuilder(); + + for (var i = 0; i < parameterSyntaxes.Count; ++i) + { + var parameterSyntax = parameterSyntaxes[i]; + var parameterName = parameterSyntax.Name.Text; + + var usedBefore = parameters.Any(p => p.Name == parameterName); + if (usedBefore) + { + // NOTE: We only report later duplicates, no need to report the first instance + binderProvider.DiagnosticBag.Add(Diagnostic.Create( + template: SymbolResolutionErrors.IllegalShadowing, + location: parameterSyntax.Location, + formatArgs: parameterName)); + } + + if (parameterSyntax.Variadic is not null && i != parameterSyntaxes.Count - 1) + { + binderProvider.DiagnosticBag.Add(Diagnostic.Create( + template: SymbolResolutionErrors.VariadicParameterNotLast, + location: parameterSyntax.Location, + formatArgs: parameterName)); + } + + var parameter = new SourceParameterSymbol(this, parameterSyntax); + parameters.Add(parameter); + } + + return parameters.ToImmutable(); + } +} From 729fc5e9d72dd3130a1045e3042fd9d6468de46d Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 11:37:47 +0100 Subject: [PATCH 39/59] Primary ctor binds, compile fails --- .../Source/SourcePrimaryConstructorSymbol.cs | 4 ++- src/Draco.Compiler/Internal/Syntax/Parser.cs | 12 ++------- src/Draco.Compiler/Internal/Syntax/Syntax.xml | 25 ++----------------- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index d3c03e8ed..70f52e47b 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -41,7 +41,9 @@ private ImmutableArray BindParametersIfNeeded(IBinderProvider b // TODO: Copypaste from SourceFunctionSymbol private ImmutableArray BindParameters(IBinderProvider binderProvider) { - var parameterSyntaxes = this.DeclaringSyntax.ParameterList.Values.ToList(); + var parameterSyntaxes = this.DeclaringSyntax.ParameterList.Values + .Select(v => v.Parameter) + .ToList(); var parameters = ImmutableArray.CreateBuilder(); for (var i = 0; i < parameterSyntaxes.Count; ++i) diff --git a/src/Draco.Compiler/Internal/Syntax/Parser.cs b/src/Draco.Compiler/Internal/Syntax/Parser.cs index 7f2458501..b6fcc9092 100644 --- a/src/Draco.Compiler/Internal/Syntax/Parser.cs +++ b/src/Draco.Compiler/Internal/Syntax/Parser.cs @@ -466,16 +466,8 @@ private PrimaryConstructorSyntax ParsePrimaryConstructor() private PrimaryConstructorParameterSyntax ParsePrimaryConstructorParameter() { var memberModifiers = this.ParsePrimaryConstructorParameterMemberModifiers(); - this.Matches(TokenKind.Ellipsis, out var variadic); - var name = this.Expect(TokenKind.Identifier); - var colon = this.Expect(TokenKind.Colon); - var type = this.ParseType(); - return new( - memberModifiers, - variadic, - name, - colon, - type); + var param = this.ParseParameter(); + return new(memberModifiers, param); } /// diff --git a/src/Draco.Compiler/Internal/Syntax/Syntax.xml b/src/Draco.Compiler/Internal/Syntax/Syntax.xml index 61204c557..8317e1075 100644 --- a/src/Draco.Compiler/Internal/Syntax/Syntax.xml +++ b/src/Draco.Compiler/Internal/Syntax/Syntax.xml @@ -330,30 +330,9 @@ Optional modifiers for the parameter, in case it is manifested as a member. - - - The ellipsis token, in case it's variadic. - - - - - + - The name of the parameter. - - - - - - - The colon token between the name and the type. - - - - - - - The type of the parameter. + The parameter declaration. From 7607ae9fa04139e0e6c248bed93b1b7f0b375e06 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 11:46:24 +0100 Subject: [PATCH 40/59] Update SourcePrimaryConstructorSymbol.cs --- .../Source/SourcePrimaryConstructorSymbol.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index 70f52e47b..f0e5aa9af 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -7,14 +7,16 @@ using Draco.Compiler.Api.Diagnostics; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Binding; +using Draco.Compiler.Internal.BoundTree; using Draco.Compiler.Internal.Symbols.Synthetized; +using static Draco.Compiler.Internal.BoundTree.BoundTreeFactory; namespace Draco.Compiler.Internal.Symbols.Source; /// /// Primary constructor defined in-source. /// -internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol +internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol, ISourceSymbol { public override TypeSymbol ContainingSymbol { get; } @@ -23,18 +25,29 @@ internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol public override bool IsStatic => false; public override bool IsSpecialName => true; public override bool IsConstructor => true; + public override Api.Semantics.Visibility Visibility => GetVisibilityFromTokenKind(this.DeclaringSyntax.VisibilityModifier?.Kind); public override ImmutableArray Parameters => this.BindParametersIfNeeded(this.DeclaringCompilation!); private ImmutableArray parameters; public override PrimaryConstructorSyntax DeclaringSyntax { get; } + public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + private BoundStatement? body; + public SourcePrimaryConstructorSymbol(TypeSymbol containingSymbol, PrimaryConstructorSyntax declaringSyntax) { this.ContainingSymbol = containingSymbol; this.DeclaringSyntax = declaringSyntax; } + public void Bind(IBinderProvider binderProvider) + { + this.BindParametersIfNeeded(binderProvider); + // Force binding of parameters, as the type is lazy too + foreach (var param in this.Parameters.Cast()) param.Bind(binderProvider); + } + private ImmutableArray BindParametersIfNeeded(IBinderProvider binderProvider) => InterlockedUtils.InitializeDefault(ref this.parameters, () => this.BindParameters(binderProvider)); @@ -75,4 +88,10 @@ private ImmutableArray BindParameters(IBinderProvider binderPro return parameters.ToImmutable(); } + + private BoundStatement BuildBody() + { + // TODO: Later we'll need to initialize fields + return ExpressionStatement(ReturnExpression(BoundUnitExpression.Default)); + } } From 8210aeded0d856fa9edbea8ee06b2b7ed794f0d0 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 18:48:59 +0100 Subject: [PATCH 41/59] Started on field --- .../Internal/Binding/Binder_Type.cs | 4 +- .../Symbols/Source/SourceClassSymbol.cs | 21 +++++++- .../Symbols/Source/SourceFieldSymbol.cs | 48 +++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs diff --git a/src/Draco.Compiler/Internal/Binding/Binder_Type.cs b/src/Draco.Compiler/Internal/Binding/Binder_Type.cs index 85f4fa7ae..12fe39b82 100644 --- a/src/Draco.Compiler/Internal/Binding/Binder_Type.cs +++ b/src/Draco.Compiler/Internal/Binding/Binder_Type.cs @@ -89,9 +89,7 @@ private Symbol BindGenericType(GenericTypeSyntax syntax, DiagnosticBag diagnosti { var instantiated = this.BindType(syntax.Instantiated, diagnostics); var args = syntax.Arguments.Values - .Select(arg => this.BindType(arg, diagnostics)) - // TODO: Why do we even need this cast? - .Cast() + .Select(arg => this.BindTypeToTypeSymbol(arg, diagnostics)) .ToImmutableArray(); if (instantiated.GenericParameters.Length != args.Length) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 9db2d6a10..0dd1ee322 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -71,20 +71,37 @@ private ImmutableArray BuildImmediateBaseTypes() return result.ToImmutable(); } + // TODO: Check for illegal shadowing private ImmutableArray BuildMembers() { var result = ImmutableArray.CreateBuilder(); - // TODO: Check for secondary constructors - // If there is no constructor, add a default one if (this.DeclaringSyntax.PrimaryConstructor is null) { + // TODO: Check for secondary constructors + // If there is no constructor, add a default one result.Add(new DefaultConstructorSymbol(this)); } else { // We have a primary constructor, add it result.Add(new SourcePrimaryConstructorSymbol(this, this.DeclaringSyntax.PrimaryConstructor)); + + // Also check for fields/props + foreach (var param in this.DeclaringSyntax.PrimaryConstructor.ParameterList.Values) + { + // Skip non-members + if (param.MemberModifiers is null) continue; + + // TODO: Implement properties + if (param.MemberModifiers.FieldModifier is null) + { + throw new NotImplementedException(); + } + + // Add field + result.Add(new SourceFieldSymbol(this, param)); + } } // Done diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs new file mode 100644 index 000000000..19b0122f1 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api.Syntax; +using Draco.Compiler.Internal.Binding; + +namespace Draco.Compiler.Internal.Symbols.Source; + +/// +/// Represents a field from source code. +/// +internal sealed class SourceFieldSymbol : FieldSymbol, ISourceSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override TypeSymbol Type => this.BindTypeIfNeeded(this.DeclaringCompilation!); + private TypeSymbol? type; + + public override string Name => this.DeclaringSyntax.Parameter.Name.Text; + public override bool IsMutable => this.DeclaringSyntax.MemberModifiers!.Keyword.Kind == TokenKind.KeywordVar; + public override Api.Semantics.Visibility Visibility => GetVisibilityFromTokenKind(this.DeclaringSyntax.MemberModifiers!.VisibilityModifier?.Kind); + + // TODO: This is not general, currently only works for fields declared in primary constructors + public override PrimaryConstructorParameterSyntax DeclaringSyntax { get; } + + public SourceFieldSymbol(TypeSymbol containingSymbol, PrimaryConstructorParameterSyntax declaringSyntax) + { + this.ContainingSymbol = containingSymbol; + this.DeclaringSyntax = declaringSyntax; + } + + public void Bind(IBinderProvider binderProvider) + { + this.BindTypeIfNeeded(binderProvider); + } + + private TypeSymbol BindTypeIfNeeded(IBinderProvider binderProvider) => + InterlockedUtils.InitializeNull(ref this.type, () => this.BindType(binderProvider)); + + private TypeSymbol BindType(IBinderProvider binderProvider) + { + var binder = binderProvider.GetBinder(this.DeclaringSyntax); + // TODO: Why do we even need this cast? + return binder.BindTypeToTypeSymbol(this.DeclaringSyntax.Parameter.Type, binderProvider.DiagnosticBag); + } +} From 3ee990863091edf39b9e56742cb5748d88f232ee Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 19:06:05 +0100 Subject: [PATCH 42/59] Fields generate in metadata --- .../Internal/Codegen/MetadataCodegen.cs | 60 ++++++++++++++----- .../Internal/OptimizingIr/ClassCodegen.cs | 3 +- .../Internal/OptimizingIr/Model/Class.cs | 6 ++ .../Internal/OptimizingIr/Model/IClass.cs | 6 +- 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index a9cacb0ad..01609c818 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -464,7 +464,18 @@ private TypeDefinitionHandle EncodeClass( var startFieldIndex = fieldIndex; var startProcIndex = procIndex; - foreach (var proc in @class.Procedures.Values) this.EncodeProcedure(proc); + foreach (var proc in @class.Procedures.Values) + { + this.EncodeProcedure(proc); + ++procIndex; + } + + foreach (var field in @class.Fields) + { + + this.EncodeField(field); + ++fieldIndex; + } // TODO: Go through the rest of the members @@ -490,13 +501,7 @@ private TypeDefinitionHandle EncodeClass( private FieldDefinitionHandle EncodeGlobal(GlobalSymbol global) { - var visibility = global.Visibility switch - { - Api.Semantics.Visibility.Public => FieldAttributes.Public, - Api.Semantics.Visibility.Internal => FieldAttributes.Assembly, - Api.Semantics.Visibility.Private => FieldAttributes.Private, - _ => throw new ArgumentOutOfRangeException(nameof(global.Visibility)), - }; + var visibility = GetFieldVisibility(global.Visibility); // Definition return this.AddFieldDefinition( @@ -505,15 +510,21 @@ private FieldDefinitionHandle EncodeGlobal(GlobalSymbol global) signature: this.EncodeGlobalSignature(global)); } + private FieldDefinitionHandle EncodeField(FieldSymbol field) + { + var visibility = GetFieldVisibility(field.Visibility); + var mutability = field.IsMutable ? default : FieldAttributes.InitOnly; + + // Definition + return this.AddFieldDefinition( + attributes: visibility | mutability, + name: field.Name, + signature: this.EncodeFieldSignature(field)); + } + private MethodDefinitionHandle EncodeProcedure(IProcedure procedure) { - var visibility = procedure.Symbol.Visibility switch - { - Api.Semantics.Visibility.Public => MethodAttributes.Public, - Api.Semantics.Visibility.Internal => MethodAttributes.Assembly, - Api.Semantics.Visibility.Private => MethodAttributes.Private, - _ => throw new ArgumentOutOfRangeException(nameof(procedure.Symbol.Visibility)), - }; + var visibility = GetMethodVisibility(procedure.Symbol.Visibility); // Encode instructions var cilCodegen = new CilCodegen(this, procedure); @@ -580,6 +591,9 @@ private MethodDefinitionHandle EncodeProcedure(IProcedure procedure) private BlobHandle EncodeGlobalSignature(GlobalSymbol global) => this.EncodeBlob(e => this.EncodeSignatureType(e.Field().Type(), global.Type)); + private BlobHandle EncodeFieldSignature(FieldSymbol field) => + this.EncodeBlob(e => this.EncodeSignatureType(e.Field().Type(), field.Type)); + private BlobHandle EncodeProcedureSignature(IProcedure procedure) => this.EncodeBlob(e => { e @@ -756,4 +770,20 @@ private void WritePe(Stream peStream) var contentId = peBuilder.Serialize(peBlob); peBlob.WriteContentTo(peStream); } + + private static FieldAttributes GetFieldVisibility(Api.Semantics.Visibility visibility) => visibility switch + { + Api.Semantics.Visibility.Public => FieldAttributes.Public, + Api.Semantics.Visibility.Internal => FieldAttributes.Assembly, + Api.Semantics.Visibility.Private => FieldAttributes.Private, + _ => throw new ArgumentOutOfRangeException(nameof(visibility)), + }; + + private static MethodAttributes GetMethodVisibility(Api.Semantics.Visibility visibility) => visibility switch + { + Api.Semantics.Visibility.Public => MethodAttributes.Public, + Api.Semantics.Visibility.Internal => MethodAttributes.Assembly, + Api.Semantics.Visibility.Private => MethodAttributes.Private, + _ => throw new ArgumentOutOfRangeException(nameof(visibility)), + }; } diff --git a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs index 1cadbf450..679ea255a 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/ClassCodegen.cs @@ -50,8 +50,7 @@ public override void VisitFunction(FunctionSymbol functionSymbol) public override void VisitField(FieldSymbol fieldSymbol) { - // TODO - throw new NotImplementedException(); + // No-op, the Class model reads it up from the symbol } // TODO: Copypasta from ModuleCodegen diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index 6c9d8eab8..f425d5d69 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -24,6 +25,11 @@ internal sealed class Class : IClass public IReadOnlyList Generics => this.Symbol.GenericParameters; + public IReadOnlyList Fields => InterlockedUtils.InitializeDefault( + ref this.fields, + () => this.Symbol.DefinedMembers.OfType().ToImmutableArray()); + private ImmutableArray fields; + public IReadOnlyDictionary Procedures => this.procedures; private readonly Dictionary procedures = new(); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs index c780fd19e..19e387f21 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -45,9 +45,13 @@ internal interface IClass // TODO: Base class // TODO: Interfaces? (we might wanna keep them external) // TODO: Nested classes - // TODO: fields // TODO: properties + /// + /// The fields defined on this class. + /// + public IReadOnlyList Fields { get; } + /// /// The procedures defined on this class. /// From 80c58debafd323b239e7ab54cef4e1b20489d10a Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Tue, 19 Dec 2023 19:38:31 +0100 Subject: [PATCH 43/59] Update SourcePrimaryConstructorSymbol.cs --- .../Source/SourcePrimaryConstructorSymbol.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index f0e5aa9af..97d8748e7 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -91,7 +91,42 @@ private ImmutableArray BindParameters(IBinderProvider binderPro private BoundStatement BuildBody() { - // TODO: Later we'll need to initialize fields - return ExpressionStatement(ReturnExpression(BoundUnitExpression.Default)); + var thisSymbol = new SynthetizedThisParameterSymbol(this); + + var statements = ImmutableArray.CreateBuilder(); + foreach (var (paramSyntax, paramSymbol) in this.DeclaringSyntax.ParameterList.Values.Zip(this.Parameters)) + { + // Skip non-members + if (paramSyntax.MemberModifiers is null) continue; + + var isField = paramSyntax.MemberModifiers.FieldModifier is not null; + var name = paramSyntax.Parameter.Name.Text; + + // Search for the field or property + // NOTE: There should _not_ be an error here, the symbols should already be present + var memberSymbol = this.ContainingSymbol.DefinedMembers + .Where(x => isField ? x is FieldSymbol : x is PropertySymbol) + .First(m => m.Name == name); + + // Build the initializer + var initializer = ExpressionStatement(AssignmentExpression( + compoundOperator: null, + // TODO: Handle properties + left: FieldLvalue( + receiver: ParameterExpression(thisSymbol), + field: (FieldSymbol)memberSymbol), + right: ParameterExpression(paramSymbol))); + + // Add it + statements.Add(initializer); + } + + // Add a return statement + statements.Add(ExpressionStatement(ReturnExpression(BoundUnitExpression.Default))); + + return ExpressionStatement(BlockExpression( + locals: ImmutableArray.Empty, + statements: statements.ToImmutable(), + value: BoundUnitExpression.Default)); } } From 76d4e619d33920692809672230ff2706ffff123c Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 10:18:01 +0100 Subject: [PATCH 44/59] Create SourceAutoPropertySymbol.cs --- .../Source/SourceAutoPropertySymbol.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs new file mode 100644 index 000000000..0d6d1de98 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Api.Syntax; +using Draco.Compiler.Internal.Binding; + +namespace Draco.Compiler.Internal.Symbols.Source; + +/// +/// An auto-prop coming from source. +/// +internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override TypeSymbol Type => throw new NotImplementedException(); + + public override FunctionSymbol? Getter => throw new NotImplementedException(); + public override FunctionSymbol? Setter => throw new NotImplementedException(); + + /// + /// The backing field of this auto-prop. + /// + public FieldSymbol BackingField => throw new NotImplementedException(); + + public override bool IsIndexer => false; + public override bool IsStatic => false; + + // TODO: Not necessarily this type, only for primary constructors + public override PrimaryConstructorParameterSyntax DeclaringSyntax { get; } + + public SourceAutoPropertySymbol(TypeSymbol containingSymbol, PrimaryConstructorParameterSyntax declaringSyntax) + { + this.ContainingSymbol = containingSymbol; + this.DeclaringSyntax = declaringSyntax; + } + + public void Bind(IBinderProvider binderProvider) => throw new NotImplementedException(); +} From 388c60ece5b2008ae9e234e227e6b92be8db195d Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 10:18:16 +0100 Subject: [PATCH 45/59] Update SourceAutoPropertySymbol.cs --- .../Internal/Symbols/Source/SourceAutoPropertySymbol.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index 0d6d1de98..5edb44d78 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -17,7 +17,7 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol public override TypeSymbol Type => throw new NotImplementedException(); - public override FunctionSymbol? Getter => throw new NotImplementedException(); + public override FunctionSymbol Getter => throw new NotImplementedException(); public override FunctionSymbol? Setter => throw new NotImplementedException(); /// From f75bf7326b3304a8351a6b37b167e88bcb8aa608 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 12:50:20 +0100 Subject: [PATCH 46/59] Update SourceAutoPropertySymbol.cs --- .../Internal/Symbols/Source/SourceAutoPropertySymbol.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index 5edb44d78..ba3bdca04 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -15,10 +15,12 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol { public override TypeSymbol ContainingSymbol { get; } - public override TypeSymbol Type => throw new NotImplementedException(); + public override TypeSymbol Type => this.BackingField.Type; public override FunctionSymbol Getter => throw new NotImplementedException(); - public override FunctionSymbol? Setter => throw new NotImplementedException(); + public override FunctionSymbol? Setter => this.Modifiers.Keyword.Kind == TokenKind.KeywordVal + ? null + : throw new NotImplementedException(); /// /// The backing field of this auto-prop. @@ -30,6 +32,7 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol // TODO: Not necessarily this type, only for primary constructors public override PrimaryConstructorParameterSyntax DeclaringSyntax { get; } + private PrimaryConstructorParameterMemberModifiersSyntax Modifiers => this.DeclaringSyntax.MemberModifiers!; public SourceAutoPropertySymbol(TypeSymbol containingSymbol, PrimaryConstructorParameterSyntax declaringSyntax) { From dcc8b9934608682fddf01bb0cfb3d8f070a79e6a Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 12:53:59 +0100 Subject: [PATCH 47/59] Added backing field --- .../Source/SourceAutoPropertySymbol.cs | 2 +- .../Synthetized/PropertyBackingFieldSymbol.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index ba3bdca04..2fb6e19ea 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -15,7 +15,7 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol { public override TypeSymbol ContainingSymbol { get; } - public override TypeSymbol Type => this.BackingField.Type; + public override TypeSymbol Type => throw new NotImplementedException(); public override FunctionSymbol Getter => throw new NotImplementedException(); public override FunctionSymbol? Setter => this.Modifiers.Keyword.Kind == TokenKind.KeywordVal diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs new file mode 100644 index 000000000..25a99dcb6 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Draco.Compiler.Internal.Symbols.Synthetized; + +/// +/// Auto-generated backing field for an auto-property. +/// +internal sealed class PropertyBackingFieldSymbol : FieldSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override TypeSymbol Type => this.Property.Type; + public override bool IsMutable => this.Property.Setter is not null; + public override string Name => $"<{this.Property.Name}>_BackingField"; + + public PropertySymbol Property { get; } + + public PropertyBackingFieldSymbol(TypeSymbol containingSymbol, PropertySymbol property) + { + this.ContainingSymbol = containingSymbol; + this.Property = property; + } +} From a5aa66e41a68bf4160b9f96b8e6aea3a7861344f Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 13:17:27 +0100 Subject: [PATCH 48/59] Updates --- .../Internal/Symbols/Source/SourceAutoPropertySymbol.cs | 2 ++ .../Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index 2fb6e19ea..cf0c710a0 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -27,8 +27,10 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol /// public FieldSymbol BackingField => throw new NotImplementedException(); + public override string Name => this.DeclaringSyntax.Parameter.Name.Text; public override bool IsIndexer => false; public override bool IsStatic => false; + public override Api.Semantics.Visibility Visibility => GetVisibilityFromTokenKind(this.Modifiers.VisibilityModifier?.Kind); // TODO: Not necessarily this type, only for primary constructors public override PrimaryConstructorParameterSyntax DeclaringSyntax { get; } diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs index 25a99dcb6..ab1fc6413 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs @@ -16,6 +16,7 @@ internal sealed class PropertyBackingFieldSymbol : FieldSymbol public override TypeSymbol Type => this.Property.Type; public override bool IsMutable => this.Property.Setter is not null; public override string Name => $"<{this.Property.Name}>_BackingField"; + public override Api.Semantics.Visibility Visibility => Api.Semantics.Visibility.Private; public PropertySymbol Property { get; } From 9b19a89bd0f210cc038d5eaf2aa88d321f7473fc Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 14:34:22 +0100 Subject: [PATCH 49/59] Mostly working props --- .../Internal/Codegen/MetadataCodegen.cs | 23 ++++++++ .../Internal/OptimizingIr/Model/Class.cs | 5 ++ .../Internal/OptimizingIr/Model/IClass.cs | 6 +- .../Source/SourceAutoPropertySymbol.cs | 33 +++++++++-- .../Symbols/Source/SourceClassSymbol.cs | 17 ++++-- .../Symbols/Source/SourceFieldSymbol.cs | 1 - .../Source/SourcePrimaryConstructorSymbol.cs | 17 +++--- ...l.cs => AutoPropertyBackingFieldSymbol.cs} | 4 +- .../Synthetized/AutoPropertyGetterSymbol.cs | 46 ++++++++++++++++ .../Synthetized/AutoPropertySetterSymbol.cs | 55 +++++++++++++++++++ 10 files changed, 186 insertions(+), 21 deletions(-) rename src/Draco.Compiler/Internal/Symbols/Synthetized/{PropertyBackingFieldSymbol.cs => AutoPropertyBackingFieldSymbol.cs} (82%) create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs create mode 100644 src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 01609c818..5a314472a 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -493,6 +493,15 @@ private TypeDefinitionHandle EncodeClass( fieldList: MetadataTokens.FieldDefinitionHandle(startFieldIndex), methodList: MetadataTokens.MethodDefinitionHandle(startProcIndex)); + // Properties + PropertyDefinitionHandle? firstProperty = null; + foreach (var prop in @class.Properties) + { + var propHandle = this.EncodeProperty(createdClass, prop); + firstProperty ??= propHandle; + } + if (firstProperty is not null) this.MetadataBuilder.AddPropertyMap(createdClass, firstProperty.Value); + // If this isn't top level module, we specify nested relationship if (parent is not null) this.MetadataBuilder.AddNestedType(createdClass, parent.Value); @@ -588,6 +597,20 @@ private MethodDefinitionHandle EncodeProcedure(IProcedure procedure) return definitionHandle; } + private PropertyDefinitionHandle EncodeProperty(TypeDefinitionHandle declaringType, PropertySymbol prop) + { + return this.MetadataBuilder.AddProperty( + attributes: PropertyAttributes.None, + name: this.GetOrAddString(prop.Name), + signature: this.EncodeBlob(e => + { + e + .PropertySignature(isInstanceProperty: !prop.IsStatic) + .Parameters(0, out var returnType, out _); + this.EncodeReturnType(returnType, prop.Type); + })); + } + private BlobHandle EncodeGlobalSignature(GlobalSymbol global) => this.EncodeBlob(e => this.EncodeSignatureType(e.Field().Type(), global.Type)); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs index f425d5d69..98a1914b5 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/Class.cs @@ -30,6 +30,11 @@ internal sealed class Class : IClass () => this.Symbol.DefinedMembers.OfType().ToImmutableArray()); private ImmutableArray fields; + public IReadOnlyList Properties => InterlockedUtils.InitializeDefault( + ref this.properties, + () => this.Symbol.DefinedMembers.OfType().ToImmutableArray()); + private ImmutableArray properties; + public IReadOnlyDictionary Procedures => this.procedures; private readonly Dictionary procedures = new(); diff --git a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs index 19e387f21..f38fc9016 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/Model/IClass.cs @@ -45,13 +45,17 @@ internal interface IClass // TODO: Base class // TODO: Interfaces? (we might wanna keep them external) // TODO: Nested classes - // TODO: properties /// /// The fields defined on this class. /// public IReadOnlyList Fields { get; } + /// + /// The properties defined on this class. + /// + public IReadOnlyList Properties { get; } + /// /// The procedures defined on this class. /// diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index cf0c710a0..137ece52c 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -3,8 +3,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Draco.Compiler.Api; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Binding; +using Draco.Compiler.Internal.Symbols.Synthetized; namespace Draco.Compiler.Internal.Symbols.Source; @@ -15,17 +17,22 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol { public override TypeSymbol ContainingSymbol { get; } - public override TypeSymbol Type => throw new NotImplementedException(); + public override TypeSymbol Type => this.BindTypeIfNeeded(this.DeclaringCompilation!); + private TypeSymbol? type; + + public override FunctionSymbol Getter => InterlockedUtils.InitializeNull(ref this.getter, this.BuildGetter); + private FunctionSymbol? getter; - public override FunctionSymbol Getter => throw new NotImplementedException(); public override FunctionSymbol? Setter => this.Modifiers.Keyword.Kind == TokenKind.KeywordVal ? null - : throw new NotImplementedException(); + : InterlockedUtils.InitializeMaybeNull(ref this.setter, this.BuildSetter); + private FunctionSymbol? setter; /// /// The backing field of this auto-prop. /// - public FieldSymbol BackingField => throw new NotImplementedException(); + public FieldSymbol BackingField => InterlockedUtils.InitializeNull(ref this.backingField, this.BuildBackingField); + private FieldSymbol? backingField; public override string Name => this.DeclaringSyntax.Parameter.Name.Text; public override bool IsIndexer => false; @@ -42,5 +49,21 @@ public SourceAutoPropertySymbol(TypeSymbol containingSymbol, PrimaryConstructorP this.DeclaringSyntax = declaringSyntax; } - public void Bind(IBinderProvider binderProvider) => throw new NotImplementedException(); + public void Bind(IBinderProvider binderProvider) + { + this.BindTypeIfNeeded(binderProvider); + } + + private TypeSymbol BindTypeIfNeeded(IBinderProvider binderProvider) => + InterlockedUtils.InitializeNull(ref this.type, () => this.BindType(binderProvider)); + + private TypeSymbol BindType(IBinderProvider binderProvider) + { + var binder = binderProvider.GetBinder(this.DeclaringSyntax); + return binder.BindTypeToTypeSymbol(this.DeclaringSyntax.Parameter.Type, binderProvider.DiagnosticBag); + } + + private FunctionSymbol BuildGetter() => new AutoPropertyGetterSymbol(this.ContainingSymbol, this); + private FunctionSymbol? BuildSetter() => new AutoPropertySetterSymbol(this.ContainingSymbol, this); + private FieldSymbol BuildBackingField() => new AutoPropertyBackingFieldSymbol(this.ContainingSymbol, this); } diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 0dd1ee322..3eaf8392f 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -93,14 +93,21 @@ private ImmutableArray BuildMembers() // Skip non-members if (param.MemberModifiers is null) continue; - // TODO: Implement properties if (param.MemberModifiers.FieldModifier is null) { - throw new NotImplementedException(); + // Property + var prop = new SourceAutoPropertySymbol(this, param); + // Add property, getter, setter and backing field + result.Add(prop); + result.Add(prop.Getter); + if (prop.Setter is not null) result.Add(prop.Setter); + result.Add(prop.BackingField); + } + else + { + // Add field + result.Add(new SourceFieldSymbol(this, param)); } - - // Add field - result.Add(new SourceFieldSymbol(this, param)); } } diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs index 19b0122f1..f1957d230 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs @@ -42,7 +42,6 @@ private TypeSymbol BindTypeIfNeeded(IBinderProvider binderProvider) => private TypeSymbol BindType(IBinderProvider binderProvider) { var binder = binderProvider.GetBinder(this.DeclaringSyntax); - // TODO: Why do we even need this cast? return binder.BindTypeToTypeSymbol(this.DeclaringSyntax.Parameter.Type, binderProvider.DiagnosticBag); } } diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index 97d8748e7..88a70c6fb 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -102,19 +102,22 @@ private BoundStatement BuildBody() var isField = paramSyntax.MemberModifiers.FieldModifier is not null; var name = paramSyntax.Parameter.Name.Text; - // Search for the field or property - // NOTE: There should _not_ be an error here, the symbols should already be present - var memberSymbol = this.ContainingSymbol.DefinedMembers - .Where(x => isField ? x is FieldSymbol : x is PropertySymbol) - .First(m => m.Name == name); + // Search for the field to initialize + FieldSymbol memberSymbol = isField + ? this.ContainingSymbol.DefinedMembers + .OfType() + .First(m => m.Name == name) + : this.ContainingSymbol.DefinedMembers + .OfType() + .First(m => m.Name == name) + .BackingField; // Build the initializer var initializer = ExpressionStatement(AssignmentExpression( compoundOperator: null, - // TODO: Handle properties left: FieldLvalue( receiver: ParameterExpression(thisSymbol), - field: (FieldSymbol)memberSymbol), + field: memberSymbol), right: ParameterExpression(paramSymbol))); // Add it diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyBackingFieldSymbol.cs similarity index 82% rename from src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs rename to src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyBackingFieldSymbol.cs index ab1fc6413..a35c60096 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/PropertyBackingFieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyBackingFieldSymbol.cs @@ -9,7 +9,7 @@ namespace Draco.Compiler.Internal.Symbols.Synthetized; /// /// Auto-generated backing field for an auto-property. /// -internal sealed class PropertyBackingFieldSymbol : FieldSymbol +internal sealed class AutoPropertyBackingFieldSymbol : FieldSymbol { public override TypeSymbol ContainingSymbol { get; } @@ -20,7 +20,7 @@ internal sealed class PropertyBackingFieldSymbol : FieldSymbol public PropertySymbol Property { get; } - public PropertyBackingFieldSymbol(TypeSymbol containingSymbol, PropertySymbol property) + public AutoPropertyBackingFieldSymbol(TypeSymbol containingSymbol, PropertySymbol property) { this.ContainingSymbol = containingSymbol; this.Property = property; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs new file mode 100644 index 000000000..c33e95208 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Internal.BoundTree; +using Draco.Compiler.Internal.Symbols.Source; +using static Draco.Compiler.Internal.BoundTree.BoundTreeFactory; + +namespace Draco.Compiler.Internal.Symbols.Synthetized; + +/// +/// An auto-generated getter for an auto-property. +/// +internal sealed class AutoPropertyGetterSymbol : FunctionSymbol, IPropertyAccessorSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override string Name => $"{this.Property.Name}_Getter"; + public override bool IsStatic => this.Property.IsStatic; + public override Api.Semantics.Visibility Visibility => this.Property.Visibility; + + public override ImmutableArray Parameters => ImmutableArray.Empty; + public override TypeSymbol ReturnType => this.Property.Type; + + public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + private BoundStatement? body; + + PropertySymbol IPropertyAccessorSymbol.Property => this.Property; + public SourceAutoPropertySymbol Property { get; } + + public AutoPropertyGetterSymbol(TypeSymbol containingSymbol, SourceAutoPropertySymbol property) + { + this.ContainingSymbol = containingSymbol; + this.Property = property; + } + + private BoundStatement BuildBody() => ExpressionStatement(BlockExpression( + locals: ImmutableArray.Empty, + statements: ImmutableArray.Create( + ExpressionStatement(ReturnExpression(FieldExpression( + receiver: ParameterExpression(new SynthetizedThisParameterSymbol(this)), + field: this.Property.BackingField)))), + value: BoundUnitExpression.Default)); +} diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs new file mode 100644 index 000000000..0219cd531 --- /dev/null +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Internal.BoundTree; +using Draco.Compiler.Internal.Symbols.Source; +using static Draco.Compiler.Internal.BoundTree.BoundTreeFactory; + +namespace Draco.Compiler.Internal.Symbols.Synthetized; + +/// +/// An auto-generated setter for an auto-property. +/// +internal sealed class AutoPropertySetterSymbol : FunctionSymbol, IPropertyAccessorSymbol +{ + public override TypeSymbol ContainingSymbol { get; } + + public override string Name => $"{this.Property.Name}_Setter"; + public override bool IsStatic => this.Property.IsStatic; + public override Api.Semantics.Visibility Visibility => this.Property.Visibility; + + public override ImmutableArray Parameters => InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); + private ImmutableArray parameters; + + public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + + public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + private BoundStatement? body; + + PropertySymbol IPropertyAccessorSymbol.Property => this.Property; + public SourceAutoPropertySymbol Property { get; } + + public AutoPropertySetterSymbol(TypeSymbol containingSymbol, SourceAutoPropertySymbol property) + { + this.ContainingSymbol = containingSymbol; + this.Property = property; + } + + private ImmutableArray BuildParameters() => + ImmutableArray.Create(new SynthetizedParameterSymbol(this, "value", this.Property.Type)); + + private BoundStatement BuildBody() => ExpressionStatement(BlockExpression( + locals: ImmutableArray.Empty, + statements: ImmutableArray.Create( + ExpressionStatement(AssignmentExpression( + compoundOperator: null, + left: FieldLvalue( + receiver: ParameterExpression(new SynthetizedThisParameterSymbol(this)), + field: this.Property.BackingField), + right: ParameterExpression(this.Parameters[0]))), + ExpressionStatement(ReturnExpression(BoundUnitExpression.Default))), + value: BoundUnitExpression.Default)); +} From 7f80da0f5913b78dcdc290cebb0419f303c5ef7a Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Thu, 21 Dec 2023 14:47:34 +0100 Subject: [PATCH 50/59] Update MetadataCodegen.cs --- .../Internal/Codegen/MetadataCodegen.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 5a314472a..21996a6b4 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -464,19 +464,6 @@ private TypeDefinitionHandle EncodeClass( var startFieldIndex = fieldIndex; var startProcIndex = procIndex; - foreach (var proc in @class.Procedures.Values) - { - this.EncodeProcedure(proc); - ++procIndex; - } - - foreach (var field in @class.Fields) - { - - this.EncodeField(field); - ++fieldIndex; - } - // TODO: Go through the rest of the members var visibility = @class.Symbol.Visibility == Api.Semantics.Visibility.Public @@ -495,13 +482,40 @@ private TypeDefinitionHandle EncodeClass( // Properties PropertyDefinitionHandle? firstProperty = null; + var propertyHandleMap = new Dictionary(); foreach (var prop in @class.Properties) { var propHandle = this.EncodeProperty(createdClass, prop); firstProperty ??= propHandle; + propertyHandleMap.Add(prop, propHandle); } if (firstProperty is not null) this.MetadataBuilder.AddPropertyMap(createdClass, firstProperty.Value); + // Procedures + foreach (var proc in @class.Procedures.Values) + { + var handle = this.EncodeProcedure(proc); + ++procIndex; + + if (proc.Symbol is IPropertyAccessorSymbol propAccessor) + { + // This is an accessor + var isGetter = propAccessor.Property.Getter == propAccessor; + this.MetadataBuilder.AddMethodSemantics( + association: propertyHandleMap[propAccessor.Property], + semantics: isGetter ? MethodSemanticsAttributes.Getter : MethodSemanticsAttributes.Setter, + methodDefinition: handle); + } + } + + // Properties + foreach (var field in @class.Fields) + { + + this.EncodeField(field); + ++fieldIndex; + } + // If this isn't top level module, we specify nested relationship if (parent is not null) this.MetadataBuilder.AddNestedType(createdClass, parent.Value); From cbfe3b1497a5760e7009eaa0e5b6d595cd4cf86b Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 18:10:32 +0100 Subject: [PATCH 51/59] Update SourceModuleSymbol.cs --- .../Internal/Symbols/Source/SourceModuleSymbol.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs index 75c33e96e..88f92760f 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceModuleSymbol.cs @@ -79,6 +79,9 @@ private ImmutableArray BindMembers(IBinderProvider binderProvider) var earlierMember = result.FirstOrDefault(s => s.Name == member.Name); result.Add(member); + // Overloading is legal, shadowing is checked by the functions themselves + if (member is FunctionSymbol && earlierMember is FunctionSymbol) continue; + // We chech for illegal shadowing if (earlierMember is null) continue; if (!earlierMember.CanBeShadowedBy(member)) continue; From 413e2673419f45f192bf912d447de94839b2d669 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 18:12:06 +0100 Subject: [PATCH 52/59] Update FunctionBodyCodegen.cs --- .../Internal/OptimizingIr/FunctionBodyCodegen.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs b/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs index 1b6a91e63..54c2c58a9 100644 --- a/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs +++ b/src/Draco.Compiler/Internal/OptimizingIr/FunctionBodyCodegen.cs @@ -70,10 +70,13 @@ private Module GetDefiningModule(Symbol symbol) private int DefineLocal(LocalSymbol local) => this.procedure.DefineLocal(local); public Register DefineRegister(TypeSymbol type) => this.procedure.DefineRegister(type); - private Procedure SynthetizeProcedure(FunctionSymbol func) + private FunctionSymbol SynthetizeProcedure(FunctionSymbol func) { Debug.Assert(func.Body is not null); + // Only synthetize, if this is the relevant place for it + if (func.ContainingSymbol != this.procedure.Symbol.ContainingSymbol) return func; + // TODO: This might not be necessarily correct, as we might be within a class // and not a module @@ -86,7 +89,7 @@ private Procedure SynthetizeProcedure(FunctionSymbol func) var codegen = new FunctionBodyCodegen(this.compilation, proc); func.Body.Accept(codegen); } - return proc; + return func; } private static bool NeedsBoxing(TypeSymbol targetType, TypeSymbol sourceType) @@ -510,7 +513,7 @@ public override IOperand VisitFunctionGroupExpression(BoundFunctionGroupExpressi // Generic functions FunctionInstanceSymbol i => this.TranslateFunctionInstanceSymbol(i), // Functions with synthetized body - FunctionSymbol f when f.DeclaringSyntax is null && f.Body is not null => this.SynthetizeProcedure(f).Symbol, + FunctionSymbol f when f.DeclaringSyntax is null && f.Body is not null => this.SynthetizeProcedure(f), // Functions with inline codegen FunctionSymbol f when f.Codegen is not null => f, // Source functions From 86f45343b984bd20da4cc7860191c284a6c7ab7c Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 18:28:40 +0100 Subject: [PATCH 53/59] Update ArrayConstructorSymbol.cs --- .../Synthetized/ArrayConstructorSymbol.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs index 35e2b1cfd..c5c901a18 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using System.Linq; using Draco.Compiler.Internal.BoundTree; -using static Draco.Compiler.Internal.BoundTree.BoundTreeFactory; +using static Draco.Compiler.Internal.OptimizingIr.InstructionFactory; namespace Draco.Compiler.Internal.Symbols.Synthetized; @@ -38,9 +38,11 @@ internal sealed class ArrayConstructorSymbol : FunctionSymbol InterlockedUtils.InitializeNull(ref this.elementType, this.BuildElementType); private TypeParameterSymbol? elementType; - public override BoundStatement Body => - InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); - private BoundStatement? body; + public override CodegenDelegate Codegen => (codegen, target, operands) => + { + var elementType = target.Type.Substitution.GenericArguments[0]; + codegen.Write(NewArray(target, elementType, operands)); + }; private readonly ArrayTypeSymbol genericArrayType; @@ -61,13 +63,4 @@ public ArrayConstructorSymbol(ArrayTypeSymbol genericArrayType) private TypeSymbol BuildReturnType() => this.genericArrayType.GenericInstantiate(this.ElementType); private TypeParameterSymbol BuildElementType() => new SynthetizedTypeParameterSymbol(this, "T"); - - private BoundStatement BuildBody() => ExpressionStatement(ReturnExpression( - value: ArrayCreationExpression( - elementType: this.ElementType, - sizes: this.Parameters - .Select(ParameterExpression) - .Cast() - .ToImmutableArray(), - type: this.ReturnType))); } From 817b0cb66736fc5d2258a5df10768bedfaec25b1 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 18:55:21 +0100 Subject: [PATCH 54/59] Started on struct --- .../Internal/Codegen/MetadataCodegen.cs | 2 ++ .../Symbols/Source/SourceClassSymbol.cs | 20 ++++++++++++++----- .../Source/SourcePrimaryConstructorSymbol.cs | 19 ++++++++++++++++-- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 21996a6b4..44f0cf9ab 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -466,10 +466,12 @@ private TypeDefinitionHandle EncodeClass( // TODO: Go through the rest of the members + // Build up attributes var visibility = @class.Symbol.Visibility == Api.Semantics.Visibility.Public ? (parent is not null ? TypeAttributes.NestedPublic : TypeAttributes.Public) : (parent is not null ? TypeAttributes.NestedAssembly : TypeAttributes.NotPublic); var attributes = visibility | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.BeforeFieldInit | TypeAttributes.Sealed; + if (@class.Symbol.IsValueType) attributes |= TypeAttributes.SequentialLayout; // Create the type var createdClass = this.AddTypeDefinition( diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index 3eaf8392f..e336aa7a7 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -65,8 +65,14 @@ private ImmutableArray BuildGenericParameters() private ImmutableArray BuildImmediateBaseTypes() { var result = ImmutableArray.CreateBuilder(); - // NOTE: For now we always just inherit from object - result.Add(this.DeclaringCompilation!.WellKnownTypes.SystemObject); + if (this.IsValueType) + { + result.Add(this.DeclaringCompilation!.WellKnownTypes.SystemValueType); + } + else + { + result.Add(this.DeclaringCompilation!.WellKnownTypes.SystemObject); + } // Done return result.ToImmutable(); } @@ -78,9 +84,13 @@ private ImmutableArray BuildMembers() if (this.DeclaringSyntax.PrimaryConstructor is null) { - // TODO: Check for secondary constructors - // If there is no constructor, add a default one - result.Add(new DefaultConstructorSymbol(this)); + // We only synthetize a default ctor, if this is a reference type + if (!this.IsValueType) + { + // TODO: Check for secondary constructors + // If there is no constructor, add a default one + result.Add(new DefaultConstructorSymbol(this)); + } } else { diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index 88a70c6fb..590eff011 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -92,8 +92,23 @@ private ImmutableArray BindParameters(IBinderProvider binderPro private BoundStatement BuildBody() { var thisSymbol = new SynthetizedThisParameterSymbol(this); - var statements = ImmutableArray.CreateBuilder(); + + // Call base constructor + if (!this.ContainingSymbol.IsValueType) + { + // We have a base type, call base constructor + var defaultCtor = this.ContainingSymbol.BaseType!.Constructors + .FirstOrDefault(ctor => ctor.Parameters.Length == 0); + // TODO: Error if base has no default constructor? + if (defaultCtor is null) throw new NotImplementedException(); + statements.Add(ExpressionStatement(CallExpression( + receiver: ParameterExpression(thisSymbol), + method: defaultCtor, + arguments: ImmutableArray.Empty))); + } + + // Member initialization foreach (var (paramSyntax, paramSymbol) in this.DeclaringSyntax.ParameterList.Values.Zip(this.Parameters)) { // Skip non-members @@ -103,7 +118,7 @@ private BoundStatement BuildBody() var name = paramSyntax.Parameter.Name.Text; // Search for the field to initialize - FieldSymbol memberSymbol = isField + var memberSymbol = isField ? this.ContainingSymbol.DefinedMembers .OfType() .First(m => m.Name == name) From b18f12f4088112ff4317137b85bcfb8691b50c39 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 18:59:37 +0100 Subject: [PATCH 55/59] Update MetadataCodegen.cs --- src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 44f0cf9ab..9fe6163a6 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -510,10 +510,9 @@ private TypeDefinitionHandle EncodeClass( } } - // Properties + // Fields foreach (var field in @class.Fields) { - this.EncodeField(field); ++fieldIndex; } From 921bb216af74b5ff3863a8b4e6252ad0ed822e10 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sat, 23 Dec 2023 19:09:49 +0100 Subject: [PATCH 56/59] Update MetadataCodegen.cs --- .../Internal/Codegen/MetadataCodegen.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs index 9fe6163a6..4acf9ac29 100644 --- a/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs +++ b/src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs @@ -478,7 +478,9 @@ private TypeDefinitionHandle EncodeClass( attributes: attributes, @namespace: default, name: @class.Name, - baseType: this.systemObjectReference, + baseType: @class.Symbol.BaseType is null + ? this.systemObjectReference + : (TypeReferenceHandle)this.GetEntityHandle(@class.Symbol.BaseType), fieldList: MetadataTokens.FieldDefinitionHandle(startFieldIndex), methodList: MetadataTokens.MethodDefinitionHandle(startProcIndex)); @@ -517,6 +519,15 @@ private TypeDefinitionHandle EncodeClass( ++fieldIndex; } + // If this is a valuetype without fields, we add .pack 0 and .size 1 + if (@class.Symbol.IsValueType && @class.Fields.Count == 0) + { + this.MetadataBuilder.AddTypeLayout( + type: createdClass, + packingSize: 0, + size: 1); + } + // If this isn't top level module, we specify nested relationship if (parent is not null) this.MetadataBuilder.AddNestedType(createdClass, parent.Value); From 54690cd574b466bb2fc618f77368dce0cb3ed1f0 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 24 Dec 2023 16:06:23 +0100 Subject: [PATCH 57/59] Update SynthetizedThisParameterSymbol.cs --- .../Synthetized/SynthetizedThisParameterSymbol.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs index c1660e574..a38d29e1f 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs @@ -12,12 +12,22 @@ namespace Draco.Compiler.Internal.Symbols.Synthetized; internal sealed class SynthetizedThisParameterSymbol : ParameterSymbol { public override FunctionSymbol ContainingSymbol { get; } - public override TypeSymbol Type => (TypeSymbol)this.ContainingSymbol.ContainingSymbol!; public override string Name => "this"; public override bool IsThis => true; + public override TypeSymbol Type => InterlockedUtils.InitializeNull(ref this.type, this.BuildType); + private TypeSymbol? type; + public SynthetizedThisParameterSymbol(FunctionSymbol containingSymbol) { this.ContainingSymbol = containingSymbol; } + + private TypeSymbol BuildType() + { + var thisType = (TypeSymbol)this.ContainingSymbol.ContainingSymbol!; + return thisType.IsValueType + ? new ReferenceTypeSymbol(thisType) + : thisType; + } } From 22622e912de43915f66a514d50d39e7394b40648 Mon Sep 17 00:00:00 2001 From: LPeter1997 Date: Sun, 24 Dec 2023 16:15:50 +0100 Subject: [PATCH 58/59] Update SourcePrimaryConstructorSymbol.cs --- .../Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index 590eff011..be8a10ab6 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -127,6 +127,8 @@ private BoundStatement BuildBody() .First(m => m.Name == name) .BackingField; + // TODO: If a property has a setter, we probably want to call that instead for + // potential side-effects // Build the initializer var initializer = ExpressionStatement(AssignmentExpression( compoundOperator: null, From 338b0fa332fcaa9e6973f8230ec5fcb32ceed16d Mon Sep 17 00:00:00 2001 From: Kuinox Date: Tue, 4 Jun 2024 22:10:17 +0200 Subject: [PATCH 59/59] Updated old API usage. --- .../Symbols/Source/SourceAutoPropertySymbol.cs | 7 ++++--- .../Internal/Symbols/Source/SourceClassSymbol.cs | 3 ++- .../Internal/Symbols/Source/SourceFieldSymbol.cs | 3 ++- .../Symbols/Source/SourcePrimaryConstructorSymbol.cs | 5 +++-- .../Symbols/Synthetized/ArrayConstructorSymbol.cs | 1 + .../Symbols/Synthetized/AutoPropertyGetterSymbol.cs | 3 ++- .../Symbols/Synthetized/AutoPropertySetterSymbol.cs | 5 +++-- .../Symbols/Synthetized/DefaultConstructorSymbol.cs | 2 +- .../Synthetized/SynthetizedThisParameterSymbol.cs | 3 ++- src/Draco.Compiler/Internal/Syntax/Parser.cs | 12 ++++++------ 10 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs index 137ece52c..ab78e3ae4 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceAutoPropertySymbol.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Draco.Compiler.Api; using Draco.Compiler.Api.Syntax; @@ -20,7 +21,7 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol public override TypeSymbol Type => this.BindTypeIfNeeded(this.DeclaringCompilation!); private TypeSymbol? type; - public override FunctionSymbol Getter => InterlockedUtils.InitializeNull(ref this.getter, this.BuildGetter); + public override FunctionSymbol Getter => LazyInitializer.EnsureInitialized(ref this.getter, this.BuildGetter); private FunctionSymbol? getter; public override FunctionSymbol? Setter => this.Modifiers.Keyword.Kind == TokenKind.KeywordVal @@ -31,7 +32,7 @@ internal sealed class SourceAutoPropertySymbol : PropertySymbol, ISourceSymbol /// /// The backing field of this auto-prop. /// - public FieldSymbol BackingField => InterlockedUtils.InitializeNull(ref this.backingField, this.BuildBackingField); + public FieldSymbol BackingField => LazyInitializer.EnsureInitialized(ref this.backingField, this.BuildBackingField); private FieldSymbol? backingField; public override string Name => this.DeclaringSyntax.Parameter.Name.Text; @@ -55,7 +56,7 @@ public void Bind(IBinderProvider binderProvider) } private TypeSymbol BindTypeIfNeeded(IBinderProvider binderProvider) => - InterlockedUtils.InitializeNull(ref this.type, () => this.BindType(binderProvider)); + LazyInitializer.EnsureInitialized(ref this.type, () => this.BindType(binderProvider)); private TypeSymbol BindType(IBinderProvider binderProvider) { diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs index e336aa7a7..f41fb22e1 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceClassSymbol.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Declarations; using Draco.Compiler.Internal.Documentation; @@ -38,7 +39,7 @@ internal sealed class SourceClassSymbol : TypeSymbol public override ClassDeclarationSyntax DeclaringSyntax => this.declaration.Syntax; - public override SymbolDocumentation Documentation => InterlockedUtils.InitializeNull(ref this.documentation, this.BuildDocumentation); + public override SymbolDocumentation Documentation => LazyInitializer.EnsureInitialized(ref this.documentation, this.BuildDocumentation); private SymbolDocumentation? documentation; private readonly ClassDeclaration declaration; diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs index f1957d230..1f2fca0e8 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourceFieldSymbol.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Draco.Compiler.Api.Syntax; using Draco.Compiler.Internal.Binding; @@ -37,7 +38,7 @@ public void Bind(IBinderProvider binderProvider) } private TypeSymbol BindTypeIfNeeded(IBinderProvider binderProvider) => - InterlockedUtils.InitializeNull(ref this.type, () => this.BindType(binderProvider)); + LazyInitializer.EnsureInitialized(ref this.type, () => this.BindType(binderProvider)); private TypeSymbol BindType(IBinderProvider binderProvider) { diff --git a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs index be8a10ab6..1ce8561e8 100644 --- a/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Source/SourcePrimaryConstructorSymbol.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Draco.Compiler.Api.Diagnostics; using Draco.Compiler.Api.Syntax; @@ -21,7 +22,7 @@ internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol, ISourceSy public override TypeSymbol ContainingSymbol { get; } public override string Name => ".ctor"; - public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + public override TypeSymbol ReturnType => WellKnownTypes.Unit; public override bool IsStatic => false; public override bool IsSpecialName => true; public override bool IsConstructor => true; @@ -32,7 +33,7 @@ internal sealed class SourcePrimaryConstructorSymbol : FunctionSymbol, ISourceSy public override PrimaryConstructorSyntax DeclaringSyntax { get; } - public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + public override BoundStatement Body => LazyInitializer.EnsureInitialized(ref this.body, this.BuildBody); private BoundStatement? body; public SourcePrimaryConstructorSymbol(TypeSymbol containingSymbol, PrimaryConstructorSyntax declaringSyntax) diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs index 7d4c899fc..ff0173d68 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/ArrayConstructorSymbol.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using System.Linq; +using System.Threading; using Draco.Compiler.Internal.BoundTree; using static Draco.Compiler.Internal.OptimizingIr.InstructionFactory; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs index c33e95208..7b59876ec 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertyGetterSymbol.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Draco.Compiler.Internal.BoundTree; using Draco.Compiler.Internal.Symbols.Source; @@ -24,7 +25,7 @@ internal sealed class AutoPropertyGetterSymbol : FunctionSymbol, IPropertyAccess public override ImmutableArray Parameters => ImmutableArray.Empty; public override TypeSymbol ReturnType => this.Property.Type; - public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + public override BoundStatement Body => LazyInitializer.EnsureInitialized(ref this.body, this.BuildBody); private BoundStatement? body; PropertySymbol IPropertyAccessorSymbol.Property => this.Property; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs index 0219cd531..6022e4551 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/AutoPropertySetterSymbol.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Draco.Compiler.Internal.BoundTree; using Draco.Compiler.Internal.Symbols.Source; @@ -24,9 +25,9 @@ internal sealed class AutoPropertySetterSymbol : FunctionSymbol, IPropertyAccess public override ImmutableArray Parameters => InterlockedUtils.InitializeDefault(ref this.parameters, this.BuildParameters); private ImmutableArray parameters; - public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + public override TypeSymbol ReturnType => WellKnownTypes.Unit; - public override BoundStatement Body => InterlockedUtils.InitializeNull(ref this.body, this.BuildBody); + public override BoundStatement Body => LazyInitializer.EnsureInitialized(ref this.body, this.BuildBody); private BoundStatement? body; PropertySymbol IPropertyAccessorSymbol.Property => this.Property; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs index 28e1e1266..e296f3119 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/DefaultConstructorSymbol.cs @@ -17,7 +17,7 @@ internal sealed class DefaultConstructorSymbol : FunctionSymbol public override TypeSymbol ContainingSymbol { get; } public override string Name => ".ctor"; - public override TypeSymbol ReturnType => IntrinsicSymbols.Unit; + public override TypeSymbol ReturnType => WellKnownTypes.Unit; public override bool IsStatic => false; public override bool IsSpecialName => true; public override bool IsConstructor => true; diff --git a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs index a38d29e1f..f936e53fc 100644 --- a/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Synthetized/SynthetizedThisParameterSymbol.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Draco.Compiler.Internal.Symbols.Synthetized; @@ -15,7 +16,7 @@ internal sealed class SynthetizedThisParameterSymbol : ParameterSymbol public override string Name => "this"; public override bool IsThis => true; - public override TypeSymbol Type => InterlockedUtils.InitializeNull(ref this.type, this.BuildType); + public override TypeSymbol Type => LazyInitializer.EnsureInitialized(ref this.type, this.BuildType); private TypeSymbol? type; public SynthetizedThisParameterSymbol(FunctionSymbol containingSymbol) diff --git a/src/Draco.Compiler/Internal/Syntax/Parser.cs b/src/Draco.Compiler/Internal/Syntax/Parser.cs index 3d8e39a59..50691eb1e 100644 --- a/src/Draco.Compiler/Internal/Syntax/Parser.cs +++ b/src/Draco.Compiler/Internal/Syntax/Parser.cs @@ -259,7 +259,7 @@ internal DeclarationSyntax ParseDeclaration(bool local = false) => /// The parsed . private DeclarationSyntax ParseDeclaration(DeclarationContext context) { - var visibility = this.ParseVisibilityModifier(); + var modifier = this.ParseVisibilityModifier(); switch (this.Peek()) { case TokenKind.KeywordImport: @@ -268,21 +268,21 @@ private DeclarationSyntax ParseDeclaration(DeclarationContext context) case TokenKind.KeywordValue: { var valueKeyword = this.Expect(TokenKind.KeywordValue); - return this.ParseClassDeclaration(visibility: visibility, valueType: valueKeyword); + return this.ParseClassDeclaration(visibility: modifier, valueType: valueKeyword); } case TokenKind.KeywordClass: - return this.ParseClassDeclaration(visibility: visibility, valueType: null); + return this.ParseClassDeclaration(visibility: modifier, valueType: null); case TokenKind.KeywordFunc: - return this.ParseFunctionDeclaration(visibility); + return this.ParseFunctionDeclaration(modifier); case TokenKind.KeywordModule: return this.ParseModuleDeclaration(context); case TokenKind.KeywordVar: case TokenKind.KeywordVal: - return this.ParseVariableDeclaration(visibility); + return this.ParseVariableDeclaration(modifier); case TokenKind.Identifier when this.Peek(1) == TokenKind.Colon: return this.ParseLabelDeclaration(context); @@ -297,7 +297,7 @@ _ when IsVisibilityModifier(t) => false, }); var info = DiagnosticInfo.Create(SyntaxErrors.UnexpectedInput, formatArgs: "declaration"); var diag = new SyntaxDiagnosticInfo(info, Offset: 0, Width: input.FullWidth); - var node = new UnexpectedDeclarationSyntax(visibility, input); + var node = new UnexpectedDeclarationSyntax(modifier, input); this.AddDiagnostic(node, diag); return node; }