Skip to content

Commit

Permalink
Multiple crashbug fixes (#491)
Browse files Browse the repository at this point in the history
* More inputs

* Update DoesNotCrashTests.cs

* Update DoesNotCrashTests.cs

* Update ConstraintSolver_Operations.cs

* Rename

* Update ConstraintSolver_Operations.cs

* Crashfix

* Update ConstraintSolver_Rules.cs

* Update ConstraintSolver_Rules.cs

* Update BoundNode.cs

* Added missing error check

* Update Binder_Statement.cs

* More bugfix

* Return expr crashfix
  • Loading branch information
LPeter1997 authored Sep 27, 2024
1 parent cd77faa commit cd2d135
Show file tree
Hide file tree
Showing 16 changed files with 348 additions and 72 deletions.
78 changes: 78 additions & 0 deletions src/Draco.Compiler.Fuzzer/inputs/gol.draco
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import System.Console;
import System.Linq.Enumerable;

val width = 80;
val height = 40;

func render(map: Array2D<bool>) {
for (y in Range(0, height)) {
for (x in Range(0, width)) {
val cell = map[x, y];
Write(if (cell) "o" else " ");
}
WriteLine();
}
}

func in_bounds(x: int32, y: int32): bool =
0 <= x < width
and 0 <= y < height;

func count_neighbors(x: int32, y: int32, map: Array2D<bool>): int32 {
func count_cell(x: int32, y: int32, map: Array2D<bool>): int32 =
if (in_bounds(x, y) and map[x, y]) 1
else 0;

var n = 0;
n += count_cell(x - 1, y - 1, map);
n += count_cell(x - 1, y, map);
n += count_cell(x - 1, y + 1, map);
n += count_cell(x, y - 1, map);
n += count_cell(x, y + 1, map);
n += count_cell(x + 1, y - 1, map);
n += count_cell(x + 1, y, map);
n += count_cell(x + 1, y + 1, map);
return n;
}

func tick(front: Array2D<bool>, back: Array2D<bool>) {
for (y in Range(0, height)) {
for (x in Range(0, width)) {
// Any live cell with two or three live neighbours survives.
// Any dead cell with three live neighbours becomes a live cell.
// All other live cells die in the next generation. Similarly, all other dead cells stay dead.
val neighbors = count_neighbors(x, y, front);
back[x, y] =
if (front[x, y]) 2 <= neighbors <= 3
else if (neighbors == 3) true
else false;
}
WriteLine();
}
}

public func main() {
var front = Array2D(width, height);
var back = Array2D(width, height);

// Glider
front[3, 3] = true;
front[4, 4] = true;
front[4, 5] = true;
front[3, 5] = true;
front[2, 5] = true;

while (true) {
tick(front, back);

Clear();
render(front);

// Swap
val temp = front;
front = back;
back = temp;

System.Threading.Thread.Sleep(200);
}
}
11 changes: 11 additions & 0 deletions src/Draco.Compiler.Fuzzer/inputs/simple_list.draco
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import System.Collections.Generic;
import System.Linq.Enumerable;
import System.Console;

func main() {
val l = List();
for (i in Range(0, 100)) {
l.Add(i);
}
WriteLine("Count = \{l.Count}");
}
9 changes: 9 additions & 0 deletions src/Draco.Compiler.Fuzzer/inputs/variadic_args.draco
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import System.Console;

func arrayOf<T>(...vs: Array<T>): Array<T> = vs;

func main() {
for (s in arrayOf("abc", "def", "ghi")) {
WriteLine(s);
}
}
5 changes: 5 additions & 0 deletions src/Draco.Compiler.Tests/EndToEnd/DoesNotCrashTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ func main() {
System.Console();
}
""")]
[InlineData("""
import System.Collections.Generic;
func f() = List();
""")]
[Theory]
public void DoesNotCrash(string source)
{
Expand Down
21 changes: 21 additions & 0 deletions src/Draco.Compiler.Tests/Semantics/SymbolResolutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4057,4 +4057,25 @@ public struct TestEnumerator
Assert.Single(diags);
AssertDiagnostics(diags, SymbolResolutionErrors.NotGettableProperty);
}

[Fact]
public void ReturningInGlobalBlockIsIllegal()
{
// val a = { return 4; };

var main = SyntaxTree.Create(CompilationUnit(
ImmutableVariableDeclaration(
"a",
null,
BlockExpression(ExpressionStatement(ReturnExpression(LiteralExpression(4)))))));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);
var diags = semanticModel.Diagnostics;

// Assert
Assert.Single(diags);
AssertDiagnostics(diags, SymbolResolutionErrors.IllegalReturn);
}
}
45 changes: 45 additions & 0 deletions src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2402,4 +2402,49 @@ public void NonCallableReportedAsIllegalExpression()
Assert.Single(diags);
AssertDiagnostics(diags, TypeCheckingErrors.IllegalExpression);
}

[Fact]
public void GenericTypeNotInstantiatedInTypeContextIsAnError()
{
// func foo(a: Array2D) {}

var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration(
"foo",
ParameterList(Parameter("a", NameType("Array2D"))),
null,
BlockFunctionBody())));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);
var diags = semanticModel.Diagnostics;

// Assert
Assert.Single(diags);
AssertDiagnostics(diags, TypeCheckingErrors.GenericTypeNotInstantiated);
}

[Fact]
public void TypeInStringInterpolationIsAnError()
{
// func foo(): string = "\{char}";

var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration(
"foo",
ParameterList(),
NameType("string"),
InlineFunctionBody(StringExpression(
LineStringStart,
[InterpolationStringPart(InterpolationStart, NameExpression("char"), InterpolationEnd)],
LineStringEnd)))));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);
var diags = semanticModel.Diagnostics;

// Assert
Assert.Single(diags);
AssertDiagnostics(diags, TypeCheckingErrors.IllegalExpression);
}
}
1 change: 1 addition & 0 deletions src/Draco.Compiler/Api/Syntax/SyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ public static TextStringPartSyntax TextStringPart(string value) =>

public static SyntaxToken LineStringStart { get; } = Token(TokenKind.LineStringStart, "\"");
public static SyntaxToken LineStringEnd { get; } = Token(TokenKind.LineStringEnd, "\"");
public static SyntaxToken InterpolationStart { get; } = Token(TokenKind.InterpolationStart, @"\{");

private static SyntaxToken Token(TokenKind tokenKind) =>
Internal.Syntax.SyntaxToken.From(tokenKind).ToRedNode(null!, null, 0);
Expand Down
9 changes: 7 additions & 2 deletions src/Draco.Compiler/Internal/Binding/Binder_Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ protected virtual void ConstraintReturnType(
ConstraintSolver constraints,
DiagnosticBag diagnostics)
{
var containingFunction = (FunctionSymbol?)this.ContainingSymbol;
Debug.Assert(containingFunction is not null);
if (this.ContainingSymbol is not FunctionSymbol containingFunction)
{
diagnostics.Add(Diagnostic.Create(
template: SymbolResolutionErrors.IllegalReturn,
location: returnSyntax.Location));
return;
}
var returnTypeSyntax = (containingFunction as SyntaxFunctionSymbol)?.DeclaringSyntax.ReturnType?.Type;
constraints.Assignable(
containingFunction.ReturnType,
Expand Down
26 changes: 23 additions & 3 deletions src/Draco.Compiler/Internal/Binding/Binder_Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ namespace Draco.Compiler.Internal.Binding;

internal partial class Binder
{
/// <summary>
/// Binds a syntax node to a value-producing expression.
/// If the expression is not a value-producing expression, an error is reported.
/// </summary>
/// <param name="syntax">The syntax to bind.</param>
/// <param name="constraints">The constraints that has been collected during the binding process.</param>
/// <param name="diagnostics">The diagnostics produced during the process.</param>
/// <returns></returns>
private async BindingTask<BoundExpression> BindExpressionToValueProducingExpression(SyntaxNode syntax, ConstraintSolver constraints, DiagnosticBag diagnostics)
{
var result = await this.BindExpression(syntax, constraints, diagnostics);
if (result.Type is null)
{
diagnostics.Add(Diagnostic.Create(
template: TypeCheckingErrors.IllegalExpression,
location: syntax.Location));
return new BoundUnexpectedExpression(syntax);
}
return result;
}

/// <summary>
/// Binds the given syntax node to an untyped expression.
/// </summary>
Expand Down Expand Up @@ -101,9 +122,8 @@ private async BindingTask<BoundExpression> BindStringExpression(StringExpression
}
case InterpolationStringPartSyntax interpolation:
{
partsTask.Add(BindingTask.FromResult<BoundStringPart>(new BoundStringInterpolation(
syntax,
await this.BindExpression(interpolation.Expression, constraints, diagnostics))));
var expr = await this.BindExpressionToValueProducingExpression(interpolation.Expression, constraints, diagnostics);
partsTask.Add(BindingTask.FromResult<BoundStringPart>(new BoundStringInterpolation(syntax, expr)));
lastNewline = false;
break;
}
Expand Down
7 changes: 4 additions & 3 deletions src/Draco.Compiler/Internal/Binding/Binder_Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ internal partial class Binder
protected virtual BindingTask<BoundStatement> BindStatement(SyntaxNode syntax, ConstraintSolver constraints, DiagnosticBag diagnostics) => syntax switch
{
// NOTE: The syntax error is already reported
UnexpectedFunctionBodySyntax or UnexpectedStatementSyntax => FromResult(new BoundUnexpectedStatement(syntax)),
UnexpectedFunctionBodySyntax
or UnexpectedStatementSyntax
or UnexpectedDeclarationSyntax => FromResult(new BoundUnexpectedStatement(syntax)),
// Ignored
ImportDeclarationSyntax => FromResult(BoundNoOpStatement.Default),
FunctionDeclarationSyntax func => this.BindFunctionDeclaration(func, constraints, diagnostics),
Expand All @@ -48,8 +50,7 @@ private BindingTask<BoundStatement> BindFunctionDeclaration(FunctionDeclarationS

private async BindingTask<BoundStatement> BindExpressionStatement(ExpressionStatementSyntax syntax, ConstraintSolver constraints, DiagnosticBag diagnostics)
{
var exprTask = this.BindExpression(syntax.Expression, constraints, diagnostics);
_ = exprTask.GetResultType(syntax.Expression, constraints, diagnostics);
var exprTask = this.BindExpressionToValueProducingExpression(syntax.Expression, constraints, diagnostics);
return new BoundExpressionStatement(syntax, await exprTask);
}

Expand Down
9 changes: 9 additions & 0 deletions src/Draco.Compiler/Internal/Binding/Binder_Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ internal TypeSymbol BindTypeToTypeSymbol(TypeSyntax syntax, DiagnosticBag diagno
var symbol = this.BindType(syntax, diagnostics);
if (symbol is TypeSymbol type)
{
// For example referencing to Array2D without the generic arguments
if (type.IsGenericDefinition)
{
diagnostics.Add(Diagnostic.Create(
template: TypeCheckingErrors.GenericTypeNotInstantiated,
location: syntax.Location,
formatArgs: type));
return WellKnownTypes.ErrorType;
}
// Ok
return type;
}
Expand Down
9 changes: 9 additions & 0 deletions src/Draco.Compiler/Internal/Binding/SymbolResolutionErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,13 @@ internal static class SymbolResolutionErrors
severity: DiagnosticSeverity.Error,
format: "the {0} {1} is inaccessible due to its visibility",
code: Code(19));

/// <summary>
/// The return expression is illegal in the current context.
/// </summary>
public static readonly DiagnosticTemplate IllegalReturn = DiagnosticTemplate.Create(
title: "illegal return",
severity: DiagnosticSeverity.Error,
format: "illegal return expression outside of function definition",
code: Code(20));
}
9 changes: 9 additions & 0 deletions src/Draco.Compiler/Internal/Binding/TypeCheckingErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,14 @@ internal static class TypeCheckingErrors
severity: DiagnosticSeverity.Error,
format: "the attribute {0} can not be applied multiple times to the same element",
code: Code(16));

/// <summary>
/// The generic type was not instantiated in a type context.
/// </summary>
public static readonly DiagnosticTemplate GenericTypeNotInstantiated = DiagnosticTemplate.Create(
title: "generic type not instantiated",
severity: DiagnosticSeverity.Error,
format: "the generic type {0} is not instantiated",
code: Code(17));
}

3 changes: 2 additions & 1 deletion src/Draco.Compiler/Internal/BoundTree/BoundNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ internal partial class BoundDelegateCreationExpression

internal partial class BoundArrayAccessExpression
{
public override TypeSymbol Type => this.Array.TypeRequired.GenericArguments[0];
public override TypeSymbol Type => this.Array.TypeRequired.GenericArguments.FirstOrDefault()
?? WellKnownTypes.ErrorType;
}

internal partial class BoundArrayLengthExpression
Expand Down
Loading

0 comments on commit cd2d135

Please sign in to comment.