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: Simplify the API for Decl members #11046

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

d10c
Copy link
Contributor

@d10c d10c commented Oct 31, 2022

  • Accessors are considered immediate members of nominal types and extensions, not just their directly enclosing VarDecl/SubscriptDecl.
  • MethodDecl relies on the above behaviour, not a special-cased getAMember() predicate.
  • Rename the user-facing IterableDeclContext to DeclWithMembers, add Decl as a superclass.
  • Maybe somehow move the DeclWithMembers.getAMember() (etc) methods to the Decl class so as to be more generally useful. But this would involve interfering with the autogenerated code in Generated::IterableDeclContext. [Update: blocked on an improved codegen script that allows us to customize generated class names independently of the swift extractor's names]
  • New method named getDeclaringDecl as an inverse of getAMember().
  • New method named getDeclaringTypeDecl that also resolves Extensions to the NominalTypeDecl that they extend. Use this to simplify MethodDecl.hasQualifiedName/2.
  • Fix failing tests:
    • library-tests/ast/no_double_parents.ql
    • library-tests/ast/PrintAst.ql
    • extractor-tests/generated/decl/ClassDecl/ClassDecl_getMember.ql
    • extractor-tests/generated/decl/EnumDecl/EnumDecl_getMember.ql

I would appreciate a critical eye on these changes, to see which ones are worth keeping or, as the case may be, redesigning. Judging from the failing tests, this is probably violating some preexisting assumptions.

d10c added 2 commits October 31, 2022 11:29
Arguably the old behaviour, of having them only be accessible
via their parent `VarDecl` or `SubscriptDecl`, was a bug.
@github-actions github-actions bot added the Swift label Oct 31, 2022
- Add the new user-facing DeclWithMembers class.
- Add Decl as a superclass, as per its name.
- Leave IterableDeclContext in place, because it gets auto-generated :/
@d10c d10c force-pushed the swift/iterabledeclcontext-cleanup branch from 20f1ee3 to b693b3d Compare October 31, 2022 14:23
Conservatively chose to make `cached` the more basic of the two, but I'm
not sure about that.
Copy link
Contributor Author

@d10c d10c left a comment

Choose a reason for hiding this comment

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

Code reviews welcome.

@@ -9,5 +9,5 @@ class VarDecl extends Generated::VarDecl {
}

class FieldDecl extends VarDecl {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe this should be PropertyDecl, to keep it consistent with the language's preferred name.

* Gets the class, struct, enum, protocol, or extension that declared
* this `Decl` as a member.
*/
cached
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a rule of thumb for when I should cache a predicate?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's always a bit difficult to judge when (and how) something should be cached. Generally, if a predicate is (1) expensive to compute, and (2) used in multiple queries, then it should most likely be cached. In this case, I don't think that the predicate should be cached as I don't think getDeclaringDecl is expensive to compute.

Copy link
Contributor

Choose a reason for hiding this comment

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

The simplest rule of thumb is: if in doubt, don't mark predicates cached when you write them (or use pragma[only_bind_into] and other hints). The optimizer doesn't usually need these tags to do a good job, and you can make things worse by using them. In the case of cached, I believe it prevents the optimizer from using the context in which the predicate is called to reduce its size, which can sometimes cost more than any gains.

The time to use these tools IMO is when you've identified a measurable performance problem and you're trying to fix it. I guess the other situation is when you're implementing or copying a familiar pattern where cached has worked out well before.

* Gets the `index`th member (0-based) of this nominal type or extension `Decl`,
* including `AccessorDecl`s of immediate members.
*/
override Decl getImmediateMember(int index) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure, but maybe this logic should be part of the extractor or codegen.

Copy link
Contributor

Choose a reason for hiding this comment

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

We generally prefer to put logic in QL when we have the choice, and there are no good reasons for putting in the extractor.

Copy link
Contributor

Choose a reason for hiding this comment

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

... I'd like to tell you why we prefer that. I think it has to do with QL being easier to see and easier to change.

private import codeql.swift.elements.decl.SubscriptDecl
private import codeql.swift.elements.decl.Decl

class DeclWithMembers extends Decl, IterableDeclContext {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still of the opinion that there's not really a need to have a class like this as part of the public AST. Rather, I'd much rather see the importing of the IterableDeclContext class disappear from swift.qll, and its use purely as an implementation detail in order to get getImmediateMember/getMember predicates on the classes that currently extend IterableDeclContext.

I think we should delay this change until we've settled on a decision here.

Copy link
Contributor

Choose a reason for hiding this comment

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

And I'm not very happy with the name DeclWithMembers. I think we're talking about a declaration of a kind that can have members, not one that necessarily does. Consider:

struct MyStruct {}

e.getAMember() = this
)
)
this.getDeclaringTypeDecl().getFullName() = typeName
Copy link
Contributor

Choose a reason for hiding this comment

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

I do like this, having getDeclaringDecl and getDeclaringTypeDecl both widely available and clearly defined is a big plus.

Comment on lines 6 to 26
class IterableDeclContext extends Generated::IterableDeclContext {
/**
* Gets the `index`th member of this iterable declaration context (0-based),
* including `AccessorDecl`s of immediate members.
*/
override Decl getImmediateMember(int index) {
result =
rank[1 + index](Decl member, Decl immMember, int immIndex, int accIndex |
immMember = super.getImmediateMember(immIndex) and
(
member = immMember and accIndex = -1
or
member = immMember.(VarDecl).getImmediateAccessorDecl(accIndex)
or
member = immMember.(SubscriptDecl).getImmediateAccessorDecl(accIndex)
)
|
member order by immIndex, accIndex
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not really sold on this change, as I do think accessors are logically and visually under a VarDecl or SubscriptDecl (and by visually I mean they are declared in subblocks of variable/subscript declarations). I would rather keep them that way.

Even if we do go with this, this requires taking them out of the children of VarDecl/SubscriptDecl, as the ast/no_double_parents.ql test failure shows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants