Skip to content
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

[Swift] Add first piece of rule reduction #2692

Merged
merged 2 commits into from
Sep 27, 2024

Conversation

stephen-hawley
Copy link

This PR adds demangling tree reduction to the project.
What does this mean?
When a symbol is demangled there is an intermediate state where there is a tree of Node which represent the semantic meaning of the symbol. In order for this to be useful to us, we need to reduce a tree of Node to a more directly accessible set of types.

To do this, there are 3 main pieces:

  1. a way to determine if a Node (and possibly subnodes) match a given pattern. This is MatchRule
  2. a way to uniformly apply rules (RulesRunner)
  3. a way, given a matched node, to reduce it to a more easily consumable data structure

This PR partially addresses issue 2664.

What happens: given a Swift5Demangler instance, calling Run with a symbol returns an IReduction that represents the result of the reduction. Right now, we're only ever instantiating either a ProtocolWitnessTableReduction, TypeSpec or a ReductionError, but I created placeholder for functions which will help enable dispatch thunks later.

Inner types will not work yet - issue opened here.

The reduction rules are non-static and the collection gets rebuilt for every symbol. This is known.

There is a name argument in the rule which is not used (yet). I'm trying to decide still if we're going to need it.

MatchRule is very flexible - it can do the simplest matching on a single Node.Kind but it can also match on multiples as well. It can match on child nodes too. Overall, this is a simplified version of a regular expression in a way. Nothing in the current code requires it, but we will need it for more complicated reductions later (thunks, for example).

Where this is going: we should be able to get all the protocol witness table symbols from a file with a single expression:

var protoWitnesses = machoFiies.AllPublicSymbols().Select(nle => new Swift5Demangler(nle.str).Run()).OfType(ProtocolWitnessTableReduction);

Similar expressions could be used to get all the dispatch thunks etc.

var symbol = "_$ThisIsJustGarbage";
var demangler = new Swift5Demangler (symbol);
var result = demangler.Run ();
var err = result as ReductionError;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is expected to happen if we would instead do result as ProtocolWitnessTableReduction? I

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that in non-test code you would always switch on the type or check for a null result after the cast. result as ProtocolWitnessTableReduction should return null if result is of the ReductionError type

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would absolutely return null. The test is there to ensure that a symbol that is obviously not a Swift mangled symbol will correctly generate an error. Anything other cast will fail.

Copy link
Member

@matouskozak matouskozak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the great descriptions, it helps a lot in understanding the PR.

Good point on future work to make the BuildMatchRules static #2691 (maybe we could include it as part of this PR already but unsure if there are some hidden obstacles that would make it too complex)

/// </summary>
internal class MatchRule {
/// <summary>
/// The name of this rule - usefule for debugging
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The name of this rule - usefule for debugging
/// The name of this rule - useful for debugging

/// <exception cref="InvalidOperationException"></exception>
bool ContentMatches(Node n)
{
// Only care about the content type not its value
Copy link
Member

@matouskozak matouskozak Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we rename it to ContentTypeMatches if we only care about the content type?

/// - One or more NodeKinds to match
/// If a match occurs, a reducer function can be run on the node.
/// </summary>
internal class MatchRule {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would be beneficial to ask someone from the Roslyn team to review this (and maybe the previously introduced Parser)?

I think this is an area commonly found in the frontend of compilers and they may have some interesting insights for us if we plan to grow this further.

/// Creates a simple string representation of this rule
/// </summary>
/// <returns></returns>
public override string ToString() => Name;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouold it help to have a DebuggerDisplayAttribute that surfaces this value?

/// </summary>
string ExpectedButGot (string expected, string butGot)
{
return $"Demangling {mangledName}: expected {expected} but got {butGot}";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these strings surface? Should they be localized?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, they should be localized. I'm not concerned with that right now. However, putting the very common "Expected but got" pattern in one place makes that easier down the road.

/// Returns a string representation of the function
/// </summary>
/// <returns></returns>
public override string ToString () => $"{Provenance}.{Name}{ParameterList} -> {Return}";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, any usefulness in having a DebuggerDisplayAttribute?

var symbol = "_$ThisIsJustGarbage";
var demangler = new Swift5Demangler (symbol);
var result = demangler.Run ();
var err = result as ReductionError;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that in non-test code you would always switch on the type or check for a null result after the cast. result as ProtocolWitnessTableReduction should return null if result is of the ReductionError type

/// <summary>
/// Represents the origin of a particular function
/// </summary>
public class Provenance {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the same property as public required BaseDecl? ParentDecl { get; set; }?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseDecl, IIRC is a C# representation not a Swift declaration. Further, extensions make this more complicated because you can have multiple extension on the same type from difference modules.

/// Represents a Swift function signature and its provenance.
/// </summary>
[DebuggerDisplay("{ToString()}")]
public class SwiftFunction {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we utilize MethodDecl and it's members instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. This is strictly Swift not C#.

@kotlarmilos kotlarmilos self-requested a review September 27, 2024 12:00
@stephen-hawley stephen-hawley merged commit 3fba66f into feature/swift-bindings Sep 27, 2024
6 checks passed
@stephen-hawley stephen-hawley deleted the tree-reduction branch September 27, 2024 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants