Skip to content

Emit 'ResolvedFromAnnotation' during type resolution #1050

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 2 commits into from
Aug 20, 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,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// Helpers to work with <see cref="ImmutableArray{T}"/>
/// </summary>
public static class ImmutableArrayHelpers
{
/// <summary>
/// Replaces any elements matching a predicate and adds a new element in one efficient operation.
/// Commonly used to replace <see cref="ISymbolAnnotation"/>s on derived symbols
/// </summary>
/// <param name="source">The original array</param>
/// <param name="old">The predicate used to find elements that need removal</param>
/// <param name="new">The new element to add</param>
/// <typeparam name="T">The type of elements in the arrays</typeparam>
/// <returns>The new array</returns>
public static ImmutableArray<T> ReplaceOrAdd<T>(this ImmutableArray<T> source, Predicate<T> old, T @new)
{
var builder = source.ToBuilder();

while (true)
{
for (var index = 0; index < builder.Count; index++)
{
if (old(builder[index]))
{
builder.RemoveAt(index);
index--;
}
}
builder.Add(@new);
// PERF: This incurs a re-allocation :/
if (builder.Capacity != builder.Count)
{
builder.Capacity = builder.Count;
}
return builder.MoveToImmutable();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.Annotations;

/// <summary>
/// An <see cref="ISymbolAnnotation"/> indicating what string a <see cref="TypeReference"/> was resolved from
/// Commonly added during type resolution. For debugging and native type discovery only. Do not use to implement multi-pass type resolution
/// </summary>
/// <param name="OriginalString">The original string the <see cref="TypeReference"/> this is referenced by was resolved from</param>
public sealed record ResolvedFromAnnotation(string OriginalString) : ISymbolAnnotation
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;

namespace Silk.NET.SilkTouch.TypeResolution;

Expand Down Expand Up @@ -47,6 +48,7 @@ protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReferenc
var types = new List<TypeReference>();
if (text[c] == '<')
{
var annotations = ImmutableArray<ISymbolAnnotation>.Empty;
c += 1;
while (true)
{
Expand All @@ -56,15 +58,15 @@ protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReferenc
var typeText = text.Substring(c, typeTextEndIndex);
c += typeTextEndIndex + 1;
if (text[c] == ' ') c++;
types.Add(new UnresolvedTypeReference(typeText, ImmutableArray<ISymbolAnnotation>.Empty));
types.Add(new UnresolvedTypeReference(typeText, annotations));
}
else
{
var l = text.Length - c - 1;
if (l > 0)
{
var typeText = text.Substring(c, l);
types.Add(new UnresolvedTypeReference(typeText, ImmutableArray<ISymbolAnnotation>.Empty));
types.Add(new UnresolvedTypeReference(typeText, annotations));
}
break;
}
Expand All @@ -80,7 +82,11 @@ protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReferenc
if (types.Count > 0)
{
resolved = new FunctionPointerTypeReference
(types.Last(), types.Take(types.Count - 1).ToImmutableArray(), utr.Annotations);
(
types.Last(),
types.Take(types.Count - 1).ToImmutableArray(),
utr.Annotations.ReplaceOrAdd(x => x is ResolvedFromAnnotation, new ResolvedFromAnnotation(text))
);
_logger.LogTrace("{text} resolved to function pointer {ptr}", utr.Text, resolved);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;

namespace Silk.NET.SilkTouch.TypeResolution;

Expand Down Expand Up @@ -61,31 +62,36 @@ protected override TypeSymbol VisitType(TypeSymbol typeSymbol)
/// <inheritdoc />
protected override TypeReference VisitTypeReference(TypeReference typeReference)
{
if (typeReference is UnresolvedTypeReference unresolvedTypeReference)
if (typeReference is UnresolvedTypeReference utr)
{
_logger.LogTrace("Attempting to resolve \"{text}\" to an internal reference", unresolvedTypeReference.Text);
if (TryFindMatchingType(_currentScope, unresolvedTypeReference.Text, out var foundDirectChild))
_logger.LogTrace("Attempting to resolve \"{text}\" to an internal reference", utr.Text);
if (TryFindMatchingType(_currentScope, utr.Text, out var foundDirectChild))
{
_logger.LogTrace("Resolved to direct child {type}", foundDirectChild);
return new InternalTypeReference(foundDirectChild!.Id, unresolvedTypeReference.Annotations);
return new InternalTypeReference(foundDirectChild!.Id, utr.Annotations);
}

// note: iterating a stack is ordered, just like repeatedly calling .Pop(), but doesn't disturb contents.
foreach (var scope in _seenScopes)
{
if (TryFindMatchingType(scope, unresolvedTypeReference.Text, out var foundChild))
if (TryFindMatchingType(scope, utr.Text, out var foundChild))
{
_logger.LogTrace
(
"Successfully resolved \"{text}\" to internal type {type}",
unresolvedTypeReference.Text,
utr.Text,
foundChild
);
return new InternalTypeReference(foundChild!.Id, unresolvedTypeReference.Annotations);
return new InternalTypeReference
(
foundChild!.Id,
utr.Annotations.ReplaceOrAdd
(x => x is ResolvedFromAnnotation, new ResolvedFromAnnotation(utr.Text))
);
}
}

return unresolvedTypeReference;
return utr;
}
return base.VisitTypeReference(typeReference);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;

namespace Silk.NET.SilkTouch.TypeResolution;

Expand All @@ -24,7 +26,16 @@ protected override TypeReference VisitTypeReference(TypeReference typeSymbol)
{
if (TryResolve(utr, out var result))
{
typeSymbol = result!;
Debug.Assert(result is not null);
if (result!.Annotations == typeSymbol.Annotations)
{
result = result with
{
Annotations = result.Annotations.ReplaceOrAdd
(x => x is ResolvedFromAnnotation, new ResolvedFromAnnotation(utr.Text))
};
}
typeSymbol = result;
continue;
}
return utr;
Expand All @@ -44,5 +55,8 @@ protected override TypeReference VisitTypeReference(TypeReference typeSymbol)
/// 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>
/// <remarks>
/// Implementors don't need to worry about adding <see cref="ResolvedFromAnnotation"/>. This is done automatically.
/// </remarks>
protected abstract bool TryResolve(UnresolvedTypeReference utr, out TypeReference? resolved);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using Xunit;

namespace Silk.NET.SilkTouch.Symbols.Tests;

public class ImmutableArrayHeloperTests
{
[Fact]
public void ReplaceOrAddRemovesAllOccurences()
{
var source = ImmutableArray.Create(5, 3, 2, 2);
var result = source.ReplaceOrAdd(x => x == 2, 8);
Assert.Equal(new[] { 5, 3, 8 }, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;
using Xunit;

namespace Silk.NET.SilkTouch.TypeResolution.Tests;
Expand All @@ -31,6 +32,7 @@ public void ShouldMatch(string text, string returnString, string[] parameters)
(new UnresolvedTypeReference(text, ImmutableArray<ISymbolAnnotation>.Empty));

var fptr = Assert.IsType<FunctionPointerTypeReference>(result);
Assert.Equal(text, Assert.IsType<ResolvedFromAnnotation>(Assert.Single(fptr.Annotations)).OriginalString);
Assert.Equal(returnString, Assert.IsType<UnresolvedTypeReference>(fptr.ReturnType).Text);
Assert.Collection
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;
using Xunit;

namespace Silk.NET.SilkTouch.TypeResolution.Tests;
Expand Down Expand Up @@ -56,5 +57,6 @@ public void SelfTypeIsResolvedCorrectly()
Assert.StrictEqual(testType.Fields[0].Identifier, @field.Identifier);
var reference = Assert.IsType<InternalTypeReference>(field.Type);
Assert.StrictEqual(@struct.Id, reference.ReferencedTypeId);
Assert.Equal("a", Assert.IsType<ResolvedFromAnnotation>(Assert.Single(reference.Annotations)).OriginalString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Immutable;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;
using Xunit;

namespace Silk.NET.SilkTouch.TypeResolution.Tests;
Expand All @@ -20,7 +21,8 @@ public void ShouldResolve(string text)
var output = new PointerTypeResolver(new TypeStore());

var finalSymbol = output.Visit(symbol);
Assert.IsType<PointerTypeReference>(finalSymbol);
var typed = Assert.IsType<PointerTypeReference>(finalSymbol);
Assert.Equal(text, Assert.IsType<ResolvedFromAnnotation>(Assert.Single(typed.Annotations)).OriginalString);
}

[Theory, Trait("Category", "Type Resolution"),
Expand Down Expand Up @@ -52,5 +54,9 @@ public void MultiPointer()
var middle = Assert.IsType<PointerTypeReference>(outer.Underlying);
var inner = Assert.IsType<PointerTypeReference>(middle.Underlying);
Assert.IsNotType<PointerTypeReference>(inner.Underlying);

Assert.Equal("int***", Assert.IsType<ResolvedFromAnnotation>(Assert.Single(outer.Annotations)).OriginalString);
Assert.Equal("int**", Assert.IsType<ResolvedFromAnnotation>(Assert.Single(middle.Annotations)).OriginalString);
Assert.Equal("int*", Assert.IsType<ResolvedFromAnnotation>(Assert.Single(inner.Annotations)).OriginalString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Immutable;
using Silk.NET.SilkTouch.Symbols;
using Silk.NET.SilkTouch.TypeResolution.Annotations;
using Xunit;

namespace Silk.NET.SilkTouch.TypeResolution.Tests;
Expand Down Expand Up @@ -36,6 +37,7 @@ public void ShouldResolve(string text, string? @namespace, string identifier)
var @ref = Assert.IsType<ExternalTypeReference>(finalSymbol);
Assert.Equal(@namespace, @ref.Namespace?.Value);
Assert.Equal(identifier, @ref.TypeIdentifier.Value);
Assert.Equal(text, Assert.IsType<ResolvedFromAnnotation>(Assert.Single(@ref.Annotations)).OriginalString);
}

[Theory, Trait("Category", "Type Resolution"),
Expand Down