Skip to content

Commit

Permalink
Basic support for aliases (#334)
Browse files Browse the repository at this point in the history
* Added type-alias

* Tore out intrinsic aliasing in codegen

* More C H A O S

* Made intrinsic forward

* Update MetadataCodegen.cs

* Many tests fixed

* Update BinderFacts.cs

* Update TypeSymbol.cs

* Correctness

* Update TypeSymbol.cs

* Update TypeCheckingTests.cs

* Works

* Cleanup

* Update ExpressionCompletionProvider.cs
  • Loading branch information
LPeter1997 authored Oct 17, 2023
1 parent faf4445 commit 3a244c5
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 171 deletions.
28 changes: 28 additions & 0 deletions src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2221,4 +2221,32 @@ public static class FooModule{
Assert.Empty(diags);
Assert.Equal("System.Random", xSym.Type.FullName);
}

[Fact]
public void StringAssignableToObject()
{
// func main(){
// var x: object = "Hello";
// }

var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration(
"main",
ParameterList(),
null,
BlockFunctionBody(
DeclarationStatement(VariableDeclaration(
"x",
NameType("object"),
StringExpression("Hello")))))));

// Act
var compilation = CreateCompilation(main);

var semanticModel = compilation.GetSemanticModel(main);

var diags = semanticModel.Diagnostics;

// Assert
Assert.Empty(diags);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public override ImmutableArray<CompletionItem> GetCompletionItems(SyntaxTree tre

private static CompletionItem? GetCompletionItem(ImmutableArray<ISymbol> symbols, CompletionContext currentContexts, SyntaxRange range) => symbols.First() switch
{
TypeSymbol when currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Type) =>
TypeSymbol or TypeAliasSymbol when currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Type) =>
CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Class),

IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
Expand Down
24 changes: 24 additions & 0 deletions src/Draco.Compiler/Api/Semantics/Symbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public interface IMemberSymbol
/// </summary>
public bool IsStatic { get; }
}

/// <summary>
/// Represents a module symbol.
/// </summary>
Expand Down Expand Up @@ -152,6 +153,17 @@ public interface ITypeSymbol : ISymbol, IMemberSymbol
{
}

/// <summary>
/// Represents a type alias symbol.
/// </summary>
public interface ITypeAliasSymbol : ISymbol, IMemberSymbol
{
/// <summary>
/// The type this alias substitutes.
/// </summary>
public ITypeSymbol Substitution { get; }
}

/// <summary>
/// Represents a type parameter symbol.
/// </summary>
Expand Down Expand Up @@ -310,6 +322,18 @@ public TypeSymbol(Internal.Symbols.TypeSymbol type)
}
}

internal sealed class TypeAliasSymbol : SymbolBase<Internal.Symbols.TypeAliasSymbol>, ITypeAliasSymbol
{
public bool IsStatic => this.Symbol.IsStatic;

public ITypeSymbol Substitution => this.Symbol.Substitution.ToApiSymbol();

public TypeAliasSymbol(Internal.Symbols.TypeAliasSymbol type)
: base(type)
{
}
}

internal sealed class TypeParameterSymbol : SymbolBase<Internal.Symbols.TypeParameterSymbol>, ITypeParameterSymbol
{
public bool IsStatic => this.Symbol.IsStatic;
Expand Down
9 changes: 6 additions & 3 deletions src/Draco.Compiler/Internal/Binding/BinderFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ or FieldSymbol
or PropertySymbol
or FunctionSymbol
or ModuleSymbol
or TypeSymbol;
or TypeSymbol
or TypeAliasSymbol;

/// <summary>
/// Checks, if a given symbol can be referenced in a type-context.
Expand All @@ -52,7 +53,8 @@ or ModuleSymbol
/// <returns>True, if <paramref name="symbol"/> can be referenced in a type-context.</returns>
public static bool IsTypeSymbol(Symbol symbol) => symbol
is TypeSymbol
or ModuleSymbol;
or ModuleSymbol
or TypeAliasSymbol;

/// <summary>
/// Checks, if a given symbol can be referenced in a label-context.
Expand All @@ -68,7 +70,8 @@ public static bool IsLabelSymbol(Symbol symbol) => symbol
/// <param name="symbol">The symbol to check.</param>
/// <returns>True, if <paramref name="symbol"/> can be referenced in a non-type value context.</returns>
public static bool IsNonTypeValueSymbol(Symbol symbol) => symbol
is not TypeSymbol && IsValueSymbol(symbol);
is not (TypeSymbol or TypeAliasSymbol)
&& IsValueSymbol(symbol);

/// <summary>
/// Retrieves the first scope defining ancestor of a given syntax node.
Expand Down
5 changes: 5 additions & 0 deletions src/Draco.Compiler/Internal/Binding/LookupResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public bool Add(Symbol symbol)
else
{
// Can be anything
if (symbol is TypeAliasSymbol typeAlias)
{
// Unwrap type alias
symbol = typeAlias.Substitution;
}
this.symbols.Add(symbol);
return true;
}
Expand Down
5 changes: 0 additions & 5 deletions src/Draco.Compiler/Internal/Codegen/MetadataCodegen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,6 @@ public EntityHandle GetEntityHandle(Symbol symbol)
{
switch (symbol)
{
// If we can translate a symbol to a metadata type, get the handle for that
// This is because primitives are encoded differently as an entity handle
case MetadataBackedPrimitiveTypeSymbol metadataSymbol:
return this.GetEntityHandle(metadataSymbol.MetadataType);

case MetadataAssemblySymbol assembly:
return this.AddAssemblyReference(assembly);

Expand Down
7 changes: 7 additions & 0 deletions src/Draco.Compiler/Internal/Symbols/Symbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ public IEnumerable<Symbol> AncestorChain
/// </summary>
public bool IsGenericInstance => this.GenericArguments.Length > 0;

/// <summary>
/// True, if this symbol is in a generic context.
/// </summary>
public bool IsInGenericContext =>
this.IsGenericDefinition
|| (this.ContainingSymbol?.IsInGenericContext ?? false);

/// <summary>
/// The metadata name of this symbol.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions src/Draco.Compiler/Internal/Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public virtual void VisitType(TypeSymbol typeSymbol)
foreach (var member in typeSymbol.Members) member.Accept(this);
}

public virtual void VisitTypeAlias(TypeAliasSymbol typeAliasSymbol)
{
typeAliasSymbol.Substitution.Accept(this);
}

public virtual void VisitTypeParameter(TypeParameterSymbol typeParameterSymbol)
{
}
Expand Down Expand Up @@ -68,6 +73,12 @@ public virtual TResult VisitType(TypeSymbol typeSymbol)
return default!;
}

public virtual TResult VisitTypeAlias(TypeAliasSymbol typeAliasSymbol)
{
typeAliasSymbol.Substitution.Accept(this);
return default!;
}

public virtual TResult VisitTypeParameter(TypeParameterSymbol typeParameterSymbol) => default!;

public virtual TResult VisitParameter(ParameterSymbol parameterSymbol) => default!;
Expand Down
74 changes: 58 additions & 16 deletions src/Draco.Compiler/Internal/Symbols/Synthetized/IntrinsicSymbols.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Draco.Compiler.Api;
using Draco.Compiler.Api.Syntax;
using Draco.Compiler.Internal.OptimizingIr;
using Draco.Compiler.Internal.OptimizingIr.Model;
using Draco.Compiler.Internal.Symbols.Error;
using static Draco.Compiler.Internal.OptimizingIr.InstructionFactory;

namespace Draco.Compiler.Internal.Symbols.Synthetized;
Expand All @@ -24,14 +21,33 @@ internal sealed partial class IntrinsicSymbols

// Types that never change

public static TypeSymbol Never => NeverTypeSymbol.Instance;
public static TypeSymbol ErrorType { get; } = new ErrorTypeSymbol("<error>");
public static TypeSymbol UninferredType { get; } = new ErrorTypeSymbol("?");
public static TypeSymbol Unit { get; } = new PrimitiveTypeSymbol("unit", isValueType: true);
public static TypeSymbol Never => WellKnownTypes.Never;
public static TypeSymbol ErrorType => WellKnownTypes.ErrorType;
public static TypeSymbol UninferredType => WellKnownTypes.UninferredType;
public static TypeSymbol Unit => WellKnownTypes.Unit;

// Types backed by metadata potentially, nonstatic
// Forwarded from metadata

public TypeSymbol Char { get; } = new PrimitiveTypeSymbol("char", isValueType: true);
public TypeSymbol Char => this.WellKnownTypes.SystemChar;
public TypeSymbol Bool => this.WellKnownTypes.SystemBoolean;

public TypeSymbol Uint8 => this.WellKnownTypes.SystemByte;
public TypeSymbol Uint16 => this.WellKnownTypes.SystemUInt16;
public TypeSymbol Uint32 => this.WellKnownTypes.SystemUInt32;
public TypeSymbol Uint64 => this.WellKnownTypes.SystemUInt64;

public TypeSymbol Int8 => this.WellKnownTypes.SystemSByte;
public TypeSymbol Int16 => this.WellKnownTypes.SystemInt16;
public TypeSymbol Int32 => this.WellKnownTypes.SystemInt32;
public TypeSymbol Int64 => this.WellKnownTypes.SystemInt64;

public TypeSymbol Float32 => this.WellKnownTypes.SystemSingle;
public TypeSymbol Float64 => this.WellKnownTypes.SystemDouble;

public TypeSymbol String => this.WellKnownTypes.SystemString;
public TypeSymbol Object => this.WellKnownTypes.SystemObject;

// Generated

public ArrayTypeSymbol Array => InterlockedUtils.InitializeNull(ref this.array, () => new(1, this.Int32));
private ArrayTypeSymbol? array;
Expand Down Expand Up @@ -59,16 +75,36 @@ public IntrinsicSymbols(Compilation compilation)
int n => new ArrayTypeSymbol(n, this.Int32).GenericInstantiate(elementType),
};

private ImmutableArray<Symbol> BuildAllSymbols() => typeof(IntrinsicSymbols)
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
.Where(prop => prop.PropertyType.IsAssignableTo(typeof(Symbol)))
.Select(prop => prop.GetValue(this))
.Cast<Symbol>()
.Concat(this.GenerateIntrinsicSymbols())
.ToImmutableArray();
private ImmutableArray<Symbol> BuildAllSymbols() =>
this.GenerateIntrinsicSymbols().ToImmutableArray();

// NOTE: We don't yield each primitive directly, we need to alias them
private IEnumerable<Symbol> GenerateIntrinsicSymbols()
{
// Primitive aliases
yield return Alias("char", this.Char);
yield return Alias("bool", this.Bool);

yield return Alias("uint8", this.Uint8);
yield return Alias("uint16", this.Uint16);
yield return Alias("uint32", this.Uint32);
yield return Alias("uint64", this.Uint64);

yield return Alias("int8", this.Int8);
yield return Alias("int16", this.Int16);
yield return Alias("int32", this.Int32);
yield return Alias("int64", this.Int64);

yield return Alias("float32", this.Float32);
yield return Alias("float64", this.Float64);

yield return Alias("string", this.String);
yield return Alias("object", this.Object);

// 1D array
yield return this.Array;
yield return this.ArrayCtor;

// Array types from 2D to 8D
for (var i = 2; i <= 8; ++i)
{
Expand All @@ -79,6 +115,9 @@ private IEnumerable<Symbol> GenerateIntrinsicSymbols()
yield return new ArrayConstructorSymbol(arrayType);
}

// Bool negation
yield return this.Bool_Not;

// Numeric operators
foreach (var type in new[]
{
Expand Down Expand Up @@ -109,6 +148,9 @@ private IEnumerable<Symbol> GenerateIntrinsicSymbols()
}
}

private static TypeAliasSymbol Alias(string name, TypeSymbol type) =>
new SynthetizedTypeAliasSymbol(name, type);

// Operators

private FunctionSymbol Unary(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Draco.Compiler.Internal.Symbols.Synthetized;
/// <summary>
/// A built-in primitive.
/// </summary>
internal class PrimitiveTypeSymbol : TypeSymbol
internal sealed class PrimitiveTypeSymbol : TypeSymbol
{
public override Symbol? ContainingSymbol => null;
public override string Name { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Draco.Compiler.Internal.Symbols.Synthetized;

/// <summary>
/// A type-alias defined by the compiler.
/// </summary>
internal sealed class SynthetizedTypeAliasSymbol : TypeAliasSymbol
{
public override string Name { get; }
public override Symbol? ContainingSymbol => null;
public override TypeSymbol Substitution { get; }

public SynthetizedTypeAliasSymbol(string name, TypeSymbol substitution)
{
this.Name = name;
this.Substitution = substitution;
}
}
20 changes: 20 additions & 0 deletions src/Draco.Compiler/Internal/Symbols/TypeAliasSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Draco.Compiler.Api.Semantics;

namespace Draco.Compiler.Internal.Symbols;

/// <summary>
/// Alias for a type. Not a real type itself.
/// </summary>
internal abstract class TypeAliasSymbol : Symbol, IMemberSymbol
{
public bool IsStatic => true;

/// <summary>
/// The type being aliased.
/// </summary>
public abstract TypeSymbol Substitution { get; }

public override void Accept(SymbolVisitor visitor) => visitor.VisitTypeAlias(this);
public override TResult Accept<TResult>(SymbolVisitor<TResult> visitor) => visitor.VisitTypeAlias(this);
public override ISymbol ToApiSymbol() => new Api.Semantics.TypeAliasSymbol(this);
}
Loading

0 comments on commit 3a244c5

Please sign in to comment.