Skip to content

Pointer Type Resolution #1011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2022
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// A <see cref="TypeReference"/> representing a pointer type
/// </summary>
/// <param name="Underlying">A reference to the pointed to type</param>
public sealed record PointerTypeReference(TypeReference Underlying) : TypeReference;
24 changes: 19 additions & 5 deletions src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protected virtual MemberSymbol VisitMember(MemberSymbol memberSymbol)
/// <param name="fieldSymbol">The field symbol to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
Expand All @@ -73,7 +73,7 @@ protected virtual FieldSymbol VisitField(FieldSymbol fieldSymbol)
/// <param name="typeReference">The type reference to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
/// <remarks>
/// By default visiting <see cref="UnresolvedTypeReference"/> will throw. Visitors involved in type resolution should override this method directly.
Expand All @@ -83,16 +83,30 @@ protected virtual TypeReference VisitTypeReference(TypeReference typeReference)
if (typeReference is ExternalTypeReference etr) return VisitExternalTypeReference(etr);
if (typeReference is InternalTypeReference itr) return VisitInternalTypeReference(itr);
if (typeReference is UnresolvedTypeReference utr) UnresolvedTypeReference.ThrowInvalidSymbol();
if (typeReference is PointerTypeReference ptr) return VisitPointerTypeReference(ptr);
return ThrowUnknownSymbol<TypeReference>(typeReference);
}

/// <summary>
/// Visit a <see cref="PointerTypeReference"/>. Will call the appropriate methods to visit the different parts of the symbol.
/// </summary>
/// <param name="pointerTypeReference">The pointer type reference to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual PointerTypeReference VisitPointerTypeReference(PointerTypeReference pointerTypeReference)
{
return new PointerTypeReference(VisitTypeReference(pointerTypeReference.Underlying));
}

/// <summary>
/// Visit a <see cref="InternalTypeReference"/>. Will call the appropriate methods to visit the different parts of the symbol.
/// </summary>
/// <param name="typeReference">The type reference to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual InternalTypeReference VisitInternalTypeReference(InternalTypeReference typeReference)
{
Expand All @@ -105,7 +119,7 @@ protected virtual InternalTypeReference VisitInternalTypeReference(InternalTypeR
/// <param name="typeReference">The type reference to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual ExternalTypeReference VisitExternalTypeReference(ExternalTypeReference typeReference)
{
Expand Down Expand Up @@ -155,7 +169,7 @@ protected virtual TypeSymbol VisitType(TypeSymbol typeSymbol)
/// <returns>The rewritten symbol</returns>
/// <seealso cref="VisitType"/>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual StructSymbol VisitStruct(StructSymbol structSymbol)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Silk.NET.SilkTouch.Symbols;

namespace Silk.NET.SilkTouch.TypeResolution;

/// <summary>
/// Resolves <see cref="UnresolvedTypeReference"/>s with Text like "a*", "a.b.c*" to a <see cref="PointerTypeReference"/> with the underlying set to an <see cref="UnresolvedTypeReference"/> with text = "a", "a.b.c", etc.
/// </summary>
/// <example>
/// <list type="table">
/// <listheader>
/// <term>Input Tree</term>
/// <description>Output Tree</description>
/// </listheader>
///
/// <item>
/// <term><see cref="UnresolvedTypeReference"/>(Text = "a.b.c*")</term>
/// <description><see cref="PointerTypeReference"/>(Underlying = <see cref="UnresolvedTypeReference"/>(Text = "a.b.c"))</description>
/// </item>
///
/// <item>
/// <term><see cref="UnresolvedTypeReference"/>(Text = "int*")</term>
/// <description><see cref="PointerTypeReference"/>(Underlying = <see cref="UnresolvedTypeReference"/>(Text = "int"))</description>
/// </item>
///
/// <item>
/// <term><see cref="UnresolvedTypeReference"/>(Text = "a")</term>
/// <description><see cref="UnresolvedTypeReference"/>(Text = "a")</description>
/// </item>
/// </list>
/// </example>
public sealed class PointerTypeResolver : SimpleTypeResolverBase
{

/// <inheritdoc />
public PointerTypeResolver(TypeStore typeStore) : base(typeStore)
{
}

/// <inheritdoc />
protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReference? resolved)
{
if (utr.Text.Length > 1 && utr.Text[utr.Text.Length - 1] == '*')
{
resolved = new PointerTypeReference(new UnresolvedTypeReference(utr.Text.Substring(0, utr.Text.Length - 1)));
return true;
}
resolved = null;
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Silk.NET.SilkTouch.Symbols;

namespace Silk.NET.SilkTouch.TypeResolution;

/// <summary>
/// A base to simplify creation of basic type resolvers that only need simple text matching.
/// </summary>
public abstract class SimpleTypeResolverBase : SymbolVisitor
{
/// <inheritdoc />
protected SimpleTypeResolverBase(TypeStore typeStore) : base(typeStore)
{
}

/// <inheritdoc />
protected override TypeReference VisitTypeReference(TypeReference typeSymbol)
{
if (typeSymbol is UnresolvedTypeReference utr)
{
return TryResolve(utr, out var result) ? result! : utr;
}
return base.VisitTypeReference(typeSymbol);
}

/// <summary>
/// Resolve an <see cref="UnresolvedTypeReference"/>
/// </summary>
/// <param name="utr">THe unresolved reference to attempt to resolve</param>
/// <param name="resolved">The resolved reference. May not be null if true is returned.</param>
/// <returns>Whether resolution was successful</returns>
/// <remarks>
/// This method is free to return a partial resolution, and still return true.
/// Partial resolutions are such that do not have a <see cref="UnresolvedTypeReference"/> at the root,
/// but have <see cref="UnresolvedTypeReference"/> somewhere in the tree.
/// </remarks>
protected abstract bool TryResolve(UnresolvedTypeReference utr, out TypeReference? resolved);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Moq;
using Moq.Protected;
using Xunit;

namespace Silk.NET.SilkTouch.Symbols.Tests.SymbolVisitorTests;

public class PointerTypeReferenceTests
{
[Fact, Trait("Category", "Type Resolution")]
public void RefIsVisitedAsSelf()
{
var symbol = new PointerTypeReference(new InternalTypeReference(TypeId.CreateNew()));

var visitor = new Mock<MockSymbolVisitor> { CallBase = true };

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<PointerTypeReference>
("VisitPointerTypeReference", Times.Once(), ItExpr.IsAny<PointerTypeReference>());
}

[Fact, Trait("Category", "Type Resolution")]
public void RefIsVisitedAsRef()
{
var symbol = new PointerTypeReference(new InternalTypeReference(TypeId.CreateNew()));

var visitor = new Mock<MockSymbolVisitor> { CallBase = true };

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<TypeReference>
("VisitTypeReference", Times.Once(), ItExpr.Is<TypeReference>(x => ReferenceEquals(x, symbol)));
}

[Fact, Trait("Category", "Type Resolution")]
public void RefUnderlyingIsVisitedAsRef()
{
var underlying = new InternalTypeReference(TypeId.CreateNew());
var symbol = new PointerTypeReference(underlying);

var visitor = new Mock<MockSymbolVisitor> { CallBase = true };

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<TypeReference>
("VisitTypeReference", Times.Once(), ItExpr.Is<TypeReference>(x => ReferenceEquals(x, underlying)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ ITestFrameworkDiscoveryOptions discoveryOptions
foreach (var (name, value) in traits)
{
if (name is "Category" &&
value is not "Integration" and not "Scraper" and not "Symbols" and not "Emitter" and not "TypeStore")
value is not "Integration" and not "Scraper" and not "Symbols" and not "Emitter" and not "TypeStore" and not "Type Resolution")
{
return this.ReportDiscoveredTestCase
(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Silk.NET.SilkTouch.Symbols;
using Xunit;

namespace Silk.NET.SilkTouch.TypeResolution.Tests;

public sealed class PointerResolverTests
{
[Theory, Trait("Category", "Type Resolution"),
InlineData("a*"),
InlineData("a.b.c*"),
InlineData("int*")
]
public void ShouldResolve(string text)
{
var symbol = new UnresolvedTypeReference(text);
var output = new PointerTypeResolver(new TypeStore());

var finalSymbol = output.Visit(symbol);
Assert.IsType<PointerTypeReference>(finalSymbol);
}

[Theory, Trait("Category", "Type Resolution"),
InlineData(""),
InlineData("a"),
InlineData("*"),
InlineData("a.b.c"),
InlineData("longType"),
InlineData("int"),
InlineData("using")
]
public void ShouldNotResolve(string text)
{
var symbol = new UnresolvedTypeReference(text);
var output = new PointerTypeResolver(new TypeStore());

var finalSymbol = output.Visit(symbol);
Assert.IsNotType<PointerTypeReference>(finalSymbol);
}
}