Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Buckle/Compiler.Tests/Assertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ internal static void AssertValue(string text, object expectedValue) {
Assert.Empty(evalResult.diagnostics.Errors().ToArray());
Assert.Equal(expectedValue, evalResult.value);

// var execDiags = compilation.Execute(false, false, out var execResult);
var execDiags = compilation.Execute(false, false, out var execResult);

// Assert.Empty(execDiags.Errors().ToArray());
// Assert.Equal(expectedValue, execResult);
Assert.Empty(execDiags.Errors().ToArray());
Assert.Equal(expectedValue, execResult);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ public sealed class EvaluatorTests {
// Local function statements
[InlineData("int A() { int B() { return 2; } return B() + 1; } return A();", 3)]
[InlineData("int A() { int B() { int A() { return 2; } return A() + 1; } return B() + 1; } return A();", 4)]
[InlineData("int A() { int a = 1; int B(int b) { return a + b; } return B(4); } return A(); ", 5)]
[InlineData("int A() { int a = 5; int B(int b) { return a + b; } return B(1); } return A(); ", 6)]
[InlineData("int A() { int a = 1; int B(int b) { return a + b; } return B(4); } return A();", 5)]
[InlineData("int A() { int a = 5; int B(int b) { return a + b; } return B(1); } return A();", 6)]
[InlineData("int A() { int a = 5; void B() { a = 6; } B(); return a; } return A();", 6)]
// Block statements and return statements
[InlineData("{ int a = 3; return a; }", 3)]
Expand Down
4 changes: 4 additions & 0 deletions src/Buckle/Compiler/CodeAnalysis/Binding/BoundProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ internal BoundProgram(
Compilation compilation,
ImmutableDictionary<MethodSymbol, BoundBlockStatement> methodBodies,
ImmutableArray<NamedTypeSymbol> types,
MultiDictionary<NamedTypeSymbol, NamedTypeSymbol> nestedTypes,
MethodSymbol entryPoint,
MethodSymbol updatePoint,
BoundProgram previous = null) {
this.compilation = compilation;
this.methodBodies = methodBodies;
this.types = types;
this.nestedTypes = nestedTypes;
this.entryPoint = entryPoint;
this.updatePoint = updatePoint;
this.previous = previous;
Expand All @@ -29,6 +31,8 @@ internal BoundProgram(

internal ImmutableArray<NamedTypeSymbol> types { get; }

internal MultiDictionary<NamedTypeSymbol, NamedTypeSymbol> nestedTypes { get; }

internal MethodSymbol entryPoint { get; }

internal MethodSymbol updatePoint { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ namespace Buckle.CodeAnalysis.Binding;

internal partial class BoundCallExpression {
internal override Symbol expressionSymbol => method;

internal bool IsConstructorInitializer() {
return method.methodKind == MethodKind.Constructor &&
receiver is not null &&
(receiver.kind is BoundKind.ThisExpression or BoundKind.BaseExpression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
<Field Name="finalPlaceholder" Type="BoundValuePlaceholder?" SkipInVisitor="true"/>
<Field Name="finalConversion" Type="BoundExpression?" SkipInVisitor="true"/>
<Field Name="resultKind" Type="LookupResultKind" PropertyOverrides="true"/>
<Field Name="originalUserDefinedOperators" Type="ImmutableArray&lt;MethodSymbol&gt;"/>
<Field Name="originalUserDefinedOperators" Type="ImmutableArray&lt;MethodSymbol&gt;" Null="allow"/>
</Node>
<Node Name="BoundNullCoalescingAssignmentOperator" Base="BoundExpression">
<Field Name="left" Type="BoundExpression"/>
Expand Down Expand Up @@ -215,7 +215,7 @@
<Field Name="resultPlaceholder" Type="BoundValuePlaceholder?" SkipInVisitor="true"/>
<Field Name="resultConversion" Type="BoundExpression?" SkipInVisitor="true"/>
<Field Name="resultKind" Type="LookupResultKind" PropertyOverrides="true"/>
<Field Name="originalUserDefinedOperators" Type="ImmutableArray&lt;MethodSymbol&gt;"/>
<Field Name="originalUserDefinedOperators" Type="ImmutableArray&lt;MethodSymbol&gt;" Null="allow"/>
</Node>
<!-- Statements -->
<Node Name="BoundErrorStatement" Base="BoundStatement">
Expand Down Expand Up @@ -250,7 +250,7 @@
<Field Name="initializer" Type="BoundStatement"/>
<Field Name="innerLocals" Type="ImmutableArray&lt;DataContainerSymbol&gt;"/>
<Field Name="condition" Type="BoundExpression?"/>
<Field Name="step" Type="BoundExpression?"/>
<Field Name="step" Type="BoundStatement?"/>
<Field Name="body" Type="BoundStatement"/>
</Node>
<Node Name="BoundGotoStatement" Base="BoundStatement">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private protected virtual List<BoundStatement> ExpandStatement(BoundStatement st
}

private protected virtual List<BoundStatement> ExpandErrorStatement(BoundErrorStatement statement) {
// Even though there is protential for expanding the childBoundNodes, it will be an error anyways so why bother
// Even though there is potential for expanding the childBoundNodes, it will be an error anyways so why bother
return [statement];
}

Expand All @@ -51,7 +51,6 @@ private protected virtual List<BoundStatement> ExpandNopStatement(BoundNopStatem
}

private protected virtual List<BoundStatement> ExpandLocalFunctionStatement(BoundLocalFunctionStatement statement) {
// ExpandBlockStatement always returns a single block statement
var newBody = (BoundBlockStatement)ExpandBlockStatement(statement.body)[0];
return [new BoundLocalFunctionStatement(statement.syntax, statement.symbol, newBody)];
}
Expand All @@ -62,7 +61,12 @@ private protected virtual List<BoundStatement> ExpandBlockStatement(BoundBlockSt
foreach (var childStatement in statement.statements)
statements.AddRange(ExpandStatement(childStatement));

return [Block(statement.syntax, statements.ToArray())];
return [new BoundBlockStatement(
statement.syntax,
statements.ToImmutableArray(),
statement.locals,
statement.localFunctions
)];
}

private protected virtual List<BoundStatement> ExpandLocalDeclarationStatement(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

namespace Buckle.CodeAnalysis.Binding;

internal abstract class BoundTreeRewriterWithStackGuard : BoundTreeRewriter {
private int _recursionDepthInternal;

private protected BoundTreeRewriterWithStackGuard() { }

private protected BoundTreeRewriterWithStackGuard(int recursionDepth) {
_recursionDepthInternal = recursionDepth;
}

private protected int _recursionDepth => _recursionDepthInternal;

internal override BoundNode Visit(BoundNode node) {
if (node is BoundExpression expression)
return VisitExpressionWithStackGuard(ref _recursionDepthInternal, expression);

return base.Visit(node);
}

private protected BoundExpression VisitExpressionWithStackGuard(BoundExpression node) {
return VisitExpressionWithStackGuard(ref _recursionDepthInternal, node);
}

private protected sealed override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) {
return (BoundExpression)base.Visit(node);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,7 @@ static bool ReceiverIsInvocation(CallExpressionSyntax node, out CallExpressionSy
}

internal override void VisitVariableDeclaration(VariableDeclarationSyntax node) {
var argumentSyntax = node.parent as ArgumentSyntax;
var argumentListSyntaxOpt = argumentSyntax?.parent as ArgumentListSyntax;
var variable = MakeDeclarationExpressionVariable(
node,
node.identifier,
argumentListSyntaxOpt,
null,
_nodeToBind
);

if (variable is not null)
_variablesBuilder.Add(variable);
VisitNodeToBind(node.initializer);
}

internal override void VisitAssignmentExpression(AssignmentExpressionSyntax node) {
Expand Down
32 changes: 7 additions & 25 deletions src/Buckle/Compiler/CodeAnalysis/Binding/ForLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ internal ForLoopBinder(Binder enclosing, ForStatementSyntax syntax)

private protected override ImmutableArray<DataContainerSymbol> BuildLocals() {
var locals = ArrayBuilder<DataContainerSymbol>.GetInstance();
ExpressionVariableFinder.FindExpressionVariables(this, locals, _syntax.initializer);
BuildLocals(this, _syntax.initializer, locals);
return locals.ToImmutableAndFree();
}

internal override BoundForStatement BindForParts(BelteDiagnosticQueue diagnostics, Binder originalBinder) {
var result = BindForParts(_syntax, originalBinder, diagnostics);
return result;
return BindForParts(_syntax, originalBinder, diagnostics);
}

private BoundForStatement BindForParts(ForStatementSyntax node, Binder originalBinder, BelteDiagnosticQueue diagnostics) {
// var initializer = originalBinder.BindStatementExpressionList(node.initializers, diagnostics);
var initializer = originalBinder.BindStatement(node.initializer, diagnostics);
BoundExpression condition = null;
var innerLocals = ImmutableArray<DataContainerSymbol>.Empty;
Expand All @@ -39,33 +37,17 @@ private BoundForStatement BindForParts(ForStatementSyntax node, Binder originalB
innerLocals = originalBinder.GetDeclaredLocalsForScope(conditionSyntax);
}

BoundExpression increment = null;
// SeparatedSyntaxList<ExpressionSyntax> incrementors = node.step;
// if (incrementors.Count > 0) {
// var scopeDesignator = incrementors.First();
// var incrementBinder = originalBinder.GetBinder(scopeDesignator);
// increment = incrementBinder.BindStatementExpressionList(incrementors, diagnostics);
// Debug.Assert(increment.Kind != BoundKind.StatementList || ((BoundStatementList)increment).Statements.Length > 1);

// var locals = incrementBinder.GetDeclaredLocalsForScope(scopeDesignator);
// if (!locals.IsEmpty) {
// if (increment.Kind == BoundKind.StatementList) {
// increment = new BoundBlock(scopeDesignator, locals, ((BoundStatementList)increment).Statements) { WasCompilerGenerated = true };
// } else {
// increment = new BoundBlock(increment.Syntax, locals, ImmutableArray.Create(increment)) { WasCompilerGenerated = true };
// }
// }
// }
BoundStatement increment = null;

if (node.step is not null) {
var scopeDesignator = node.step;
var incrementBinder = originalBinder.GetBinder(scopeDesignator);
increment = incrementBinder.BindExpression(node.step, diagnostics);
increment = new BoundExpressionStatement(node.step, incrementBinder.BindExpression(node.step, diagnostics));

var locals = incrementBinder.GetDeclaredLocalsForScope(scopeDesignator);

if (!locals.IsEmpty) {
// increment = new BoundBlock(increment.Syntax, locals, ImmutableArray.Create(increment)) { WasCompilerGenerated = true };
}
if (!locals.IsEmpty)
increment = new BoundBlockStatement(increment.syntax, [increment], locals, []);
}

var body = originalBinder.BindPossibleEmbeddedStatement(node.body, diagnostics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,8 @@ void EmitArgumentsAndCallEpilogue(BoundCallExpression call, CallKind callKind, U
if (callKind == CallKind.CallVirt) {
if (IsThisReceiver(receiver) && actualMethodTargetedByTheCall.containingType.isSealed)
callKind = CallKind.Call;
else if (actualMethodTargetedByTheCall.isMetadataFinal && CanUseCallOnRefTypeReceiver(receiver))
callKind = CallKind.Call;
}

var arguments = call.arguments;
Expand Down Expand Up @@ -2538,6 +2540,9 @@ private void EmitFieldLoad(BoundFieldAccessExpression expression, bool used) {
var field = expression.field;

if (!used) {
if (field.isCapturedFrame)
return;

if (!field.isStatic && expression.receiver.type.IsVerifierValue() && field.refKind == RefKind.None) {
EmitExpression(expression.receiver, used: false);
return;
Expand Down Expand Up @@ -2700,10 +2705,17 @@ private void EmitCastExpression(BoundCastExpression expression, bool used) {
}

private void EmitCast(BoundCastExpression cast) {
if (IsReferenceType(cast.operand.type) && cast.type.specialType == SpecialType.Nullable)
return;
if (IsReferenceType(cast.operand.type)) {
if (cast.type.specialType == SpecialType.Nullable) {
return;
} else if (cast.type.specialType == SpecialType.String) {
_builder.EmitToString();
return;
}
}

var involvesRefTypes = cast.operand.type.IsVerifierReference() || cast.type.IsVerifierReference();
var involvesRefTypes = cast.operand.type.IsVerifierReference() ||
(cast.type.IsVerifierReference() && cast.type.specialType != SpecialType.String);

switch (cast.conversion.kind) {
case ConversionKind.Identity:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ internal abstract class ILBuilder {

internal abstract void EmitArrayCreate(ArrayTypeSymbol type);

internal abstract void EmitToString();

internal abstract VariableDefinition AllocateTemp(TypeSymbol type, bool isRef);

internal abstract VariableDefinition GetLocal(DataContainerSymbol local);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@
namespace Buckle.CodeAnalysis;

internal static class ImmutableArrayExtensions {
internal static int BinarySearch<TElement, TValue>(
this ImmutableArray<TElement> array,
TValue value,
Func<TElement, TValue, int> comparer)
=> BinarySearch(array.AsSpan(), value, comparer);

internal static int BinarySearch<TElement, TValue>(
this ReadOnlySpan<TElement> array,
TValue value,
Func<TElement, TValue, int> comparer) {
var low = 0;
var high = array.Length - 1;

while (low <= high) {
var middle = low + ((high - low) >> 1);
var comparison = comparer(array[middle], value);

if (comparison == 0)
return middle;

if (comparison > 0)
high = middle - 1;
else
low = middle + 1;
}

return ~low;
}

internal static ImmutableArray<T> AsImmutable<T>(this IEnumerable<T> items) {
return [.. items];
}
Expand Down
3 changes: 3 additions & 0 deletions src/Buckle/Compiler/CodeAnalysis/Compilation/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ internal void Evaluate(

Log(logTime, timer, diagnostics, $"Evaluated the program in {timer?.ElapsedMilliseconds} ms");

if (verbose && options.enableOutput && evalResult is not null)
Console.WriteLine(evalResult);

if (rollingResult is null) {
rollingResult = new EvaluationResult(
evalResult,
Expand Down
Loading