diff --git a/src/Draco.Compiler/Internal/Binding/Binder_BoundExpression.cs b/src/Draco.Compiler/Internal/Binding/Binder_BoundExpression.cs index 615979a3b..537a4c212 100644 --- a/src/Draco.Compiler/Internal/Binding/Binder_BoundExpression.cs +++ b/src/Draco.Compiler/Internal/Binding/Binder_BoundExpression.cs @@ -52,6 +52,7 @@ internal partial class Binder UntypedAndExpression and => this.TypeAndExpression(and, constraints, diagnostics), UntypedOrExpression or => this.TypeOrExpression(or, constraints, diagnostics), UntypedMemberExpression mem => this.TypeMemberExpression(mem, constraints, diagnostics), + UntypedMatchExpression match => this.TypeMatchExpression(match, constraints, diagnostics), UntypedDelayedExpression delay => this.TypeDelayedExpression(delay, constraints, diagnostics), _ => throw new ArgumentOutOfRangeException(nameof(expression)), }; @@ -402,6 +403,25 @@ private BoundExpression TypeMemberExpression(UntypedMemberExpression mem, Constr } } + private BoundExpression TypeMatchExpression(UntypedMatchExpression match, ConstraintSolver constraints, DiagnosticBag diagnostics) + { + var matchedValue = this.TypeExpression(match.MatchedValue, constraints, diagnostics); + + var arms = ImmutableArray.CreateBuilder(); + foreach (var arm in match.MatchArms) + { + var pattern = this.TypePattern(arm.Pattern, constraints, diagnostics); + var guard = arm.Guard is null + ? null + : this.TypeExpression(arm.Guard, constraints, diagnostics); + var value = this.TypeExpression(arm.Value, constraints, diagnostics); + + arms.Add(new(arm.Syntax, pattern, guard, value)); + } + + return new BoundMatchExpression(match.Syntax, matchedValue, arms.ToImmutable(), match.TypeRequired); + } + private BoundExpression TypeDelayedExpression(UntypedDelayedExpression delay, ConstraintSolver constraints, DiagnosticBag diagnostics) { // Just take result and type that diff --git a/src/Draco.Compiler/Internal/Binding/Binder_BoundPattern.cs b/src/Draco.Compiler/Internal/Binding/Binder_BoundPattern.cs new file mode 100644 index 000000000..86c9d2d83 --- /dev/null +++ b/src/Draco.Compiler/Internal/Binding/Binder_BoundPattern.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Draco.Compiler.Internal.BoundTree; +using Draco.Compiler.Internal.Diagnostics; +using Draco.Compiler.Internal.Solver; +using Draco.Compiler.Internal.UntypedTree; + +namespace Draco.Compiler.Internal.Binding; + +internal partial class Binder +{ + /// + /// Binds the given untyped pattern to a bound pattern. + /// + /// The untyped pattern to bind. + /// The constraints that has been collected during the binding process. + /// The diagnostics produced during the process. + /// The bound expression for . + internal virtual BoundPattern TypePattern(UntypedPattern pattern, ConstraintSolver constraints, DiagnosticBag diagnostics) => pattern switch + { + UntypedDiscardPattern discard => this.TypeDiscardPattern(discard, constraints, diagnostics), + UntypedLiteralPattern literal => this.TypeLiteralPattern(literal, constraints, diagnostics), + _ => throw new ArgumentOutOfRangeException(nameof(pattern)), + }; + + private BoundPattern TypeDiscardPattern(UntypedDiscardPattern discard, ConstraintSolver constraints, DiagnosticBag diagnostics) => + new BoundDiscardPattern(discard.Syntax); + + private BoundPattern TypeLiteralPattern(UntypedLiteralPattern literal, ConstraintSolver constraints, DiagnosticBag diagnostics) => + new BoundLiteralPattern(literal.Syntax, literal.Value, literal.Type); +} diff --git a/src/Draco.Compiler/Internal/Binding/Binder_UntypedExpression.cs b/src/Draco.Compiler/Internal/Binding/Binder_UntypedExpression.cs index c43165169..1524f1fe3 100644 --- a/src/Draco.Compiler/Internal/Binding/Binder_UntypedExpression.cs +++ b/src/Draco.Compiler/Internal/Binding/Binder_UntypedExpression.cs @@ -46,6 +46,7 @@ internal partial class Binder MemberExpressionSyntax maccess => this.BindMemberExpression(maccess, constraints, diagnostics), GenericExpressionSyntax gen => this.BindGenericExpression(gen, constraints, diagnostics), IndexExpressionSyntax index => this.BindIndexExpression(index, constraints, diagnostics), + MatchExpressionSyntax match => this.BindMatchExpression(match, constraints, diagnostics), _ => throw new ArgumentOutOfRangeException(nameof(syntax)), }; @@ -736,6 +737,33 @@ private UntypedExpression BindGenericExpression(GenericExpressionSyntax syntax, } } + private UntypedExpression BindMatchExpression(MatchExpressionSyntax syntax, ConstraintSolver constraints, DiagnosticBag diagnostics) + { + var matchedValue = this.BindExpression(syntax.MatchedValue, constraints, diagnostics); + + var arms = ImmutableArray.CreateBuilder(); + var armValueTypes = ImmutableArray.CreateBuilder(); + + foreach (var armSyntax in syntax.MatchArms) + { + var pattern = this.BindPattern(armSyntax.Pattern, constraints, diagnostics); + constraints.Assignable(pattern.Type, matchedValue.TypeRequired, armSyntax.Pattern); + + var guard = armSyntax.GuardClause is null + ? null + : this.BindExpression(armSyntax.GuardClause.Condition, constraints, diagnostics); + var value = this.BindExpression(armSyntax.Value, constraints, diagnostics); + armValueTypes.Add(value.TypeRequired); + + arms.Add(new(armSyntax, pattern, guard, value)); + } + + var commonType = constraints.AllocateTypeVariable(); + constraints.CommonType(commonType, armValueTypes.ToImmutable(), syntax); + + return new UntypedMatchExpression(syntax, matchedValue, arms.ToImmutable(), commonType); + } + private UntypedExpression SymbolToExpression(SyntaxNode syntax, Symbol symbol, ConstraintSolver constraints, DiagnosticBag diagnostics) { if (symbol.IsError) return new UntypedReferenceErrorExpression(syntax, symbol); diff --git a/src/Draco.Compiler/Internal/Binding/Binder_UntypedPattern.cs b/src/Draco.Compiler/Internal/Binding/Binder_UntypedPattern.cs new file mode 100644 index 000000000..bab1b9fca --- /dev/null +++ b/src/Draco.Compiler/Internal/Binding/Binder_UntypedPattern.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.Syntax; +using Draco.Compiler.Internal.Diagnostics; +using Draco.Compiler.Internal.Solver; +using Draco.Compiler.Internal.UntypedTree; + +namespace Draco.Compiler.Internal.Binding; + +internal partial class Binder +{ + /// + /// Binds the given syntax node to an untyped pattern. + /// + /// The syntax to bind. + /// The constraints that has been collected during the binding process. + /// The diagnostics produced during the process. + /// The untyped pattern for . + protected virtual UntypedPattern BindPattern(SyntaxNode syntax, ConstraintSolver constraints, DiagnosticBag diagnostics) => syntax switch + { + DiscardPatternSyntax discard => this.BindDiscardPattern(discard, constraints, diagnostics), + LiteralPatternSyntax literal => this.BindLiteralPattern(literal, constraints, diagnostics), + _ => throw new ArgumentOutOfRangeException(nameof(syntax)), + }; + + private UntypedPattern BindDiscardPattern(DiscardPatternSyntax syntax, ConstraintSolver constraints, DiagnosticBag diagnostics) => + new UntypedDiscardPattern(syntax); + + private UntypedPattern BindLiteralPattern(LiteralPatternSyntax syntax, ConstraintSolver constraints, DiagnosticBag diagnostics) + { + if (!BinderFacts.TryGetLiteralType(syntax.Literal.Value, this.IntrinsicSymbols, out var literalType)) + { + throw new InvalidOperationException("can not determine literal type"); + } + return new UntypedLiteralPattern(syntax, syntax.Literal.Value, literalType); + } +} diff --git a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs index 3b2fc51bc..fd679e1ce 100644 --- a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs +++ b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs @@ -204,3 +204,8 @@ internal partial class BoundPattern { public abstract TypeSymbol Type { get; } } + +internal partial class BoundDiscardPattern +{ + public override TypeSymbol Type => IntrinsicSymbols.Never; +} diff --git a/src/Draco.Compiler/Internal/BoundTree/BoundNodes.xml b/src/Draco.Compiler/Internal/BoundTree/BoundNodes.xml index 3d102c1ac..6675c1fe2 100644 --- a/src/Draco.Compiler/Internal/BoundTree/BoundNodes.xml +++ b/src/Draco.Compiler/Internal/BoundTree/BoundNodes.xml @@ -305,7 +305,6 @@ - diff --git a/src/Draco.Compiler/Internal/UntypedTree/UntypedNode.cs b/src/Draco.Compiler/Internal/UntypedTree/UntypedNode.cs index b9f59171f..d87f13010 100644 --- a/src/Draco.Compiler/Internal/UntypedTree/UntypedNode.cs +++ b/src/Draco.Compiler/Internal/UntypedTree/UntypedNode.cs @@ -142,3 +142,8 @@ internal partial class UntypedPattern { public abstract TypeSymbol Type { get; } } + +internal partial class UntypedDiscardPattern +{ + public override TypeSymbol Type => IntrinsicSymbols.Never; +} diff --git a/src/Draco.Compiler/Internal/UntypedTree/UntypedNodes.xml b/src/Draco.Compiler/Internal/UntypedTree/UntypedNodes.xml index b40a54093..6106c62b2 100644 --- a/src/Draco.Compiler/Internal/UntypedTree/UntypedNodes.xml +++ b/src/Draco.Compiler/Internal/UntypedTree/UntypedNodes.xml @@ -279,7 +279,6 @@ -