-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Introduce special DeclNames for subscripts #6848
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
Conversation
@jrose-apple: I've taken a more conservative approach now. Could you maybe have a quick look if this is going in the right direction? It's still kind of WIP and I haven't created the matching PR to the LLDB repo yet, so no need for running the test or doing an in-depth review. Thanks! |
I'm making fairly good progress on this PR now and just have a few issues to fix. For that, I've got a quick question: Subscripts are currently represented in SIL as
namely Until now this wasn't an issue since both of them were represented by the identifier Do you think this syntax would fit in the current spirit of SIL (which I really don't know a lot about) or is there another syntax coming to your mind that would be better? Thanks! |
34ed74c
to
4229f92
Compare
Sorry for continuing to ignore this series, even after Branch Day—lots of 3.1 bugs to keep our hands full! I'm no SIL expert, but I think expanding the set of allowed characters seems a bit iffy. Don't we have the same problem with |
Or `subscript`!1 ? |
8049fbc
to
4f6855b
Compare
Please test with following pull request: @swift-ci Please test |
Build failed |
Build failed |
4f6855b
to
87bfcd8
Compare
Please test with following pull request: @swift-ci Please test |
Build failed |
Build failed |
Hitting this failure: https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20170130/003954.html Please test with following pull request: @swift-ci Please test |
@jrose-apple: The PR is done so far from my side, could you (or someone else) review the changes? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for continuing to push this forward, Alex. I find I'm still really uncomfortable with this change because it erases the distinction between operations that take compound names and those that only take base names (whether normal or special). I'm going to go back to saying that we need to change Identifier, not DeclName. @DougGregor?
include/swift/AST/Decl.h
Outdated
@@ -4698,6 +4700,8 @@ class AbstractFunctionDecl : public ValueDecl, public DeclContext { | |||
GenericEnvironment *getLazyGenericEnvironmentSlow() const; | |||
|
|||
public: | |||
Identifier getName() const { return getFullName().getBaseName(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one I'd be happy to drop in favor of getBaseName()
, but that can be a follow-up patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I wanted to keep the code change as small as possible and not litter the PR with renames from getName
to getBaseName
.
include/swift/AST/Identifier.h
Outdated
StringRef str() const { | ||
switch (getKind()) { | ||
case DeclNameKind::Normal: | ||
return getBaseName().str(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's okay to drop the argument labels here. Either this should be called getBaseNameStr()
(or something), or it should assert that this is not a compound name (and probably still have a less simple name than str()
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming sounds like the way to go since IIRC this behaviour is needed in quite a few places.
lib/AST/ASTMangler.cpp
Outdated
@@ -1733,7 +1734,7 @@ void ASTMangler::appendProtocolConformance(const ProtocolConformance *conformanc | |||
appendIdentifier( | |||
fileUnit->getDiscriminatorForPrivateValue(behaviorStorage).str()); | |||
appendProtocolName(conformance->getProtocol()); | |||
appendIdentifier(behaviorStorage->getName().str()); | |||
appendIdentifier(behaviorStorage->getBaseName().getBaseName().str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra getBaseName
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This basically asserts that the base name is not special because then the second getBaseName()
will crash. Maybe an assert
would indeed be cleaner.
lib/AST/Decl.cpp
Outdated
@@ -190,7 +190,7 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const { | |||
return DescriptiveDeclKind::MaterializeForSet; | |||
} | |||
|
|||
if (!func->getName().empty() && func->getName().isOperator()) | |||
if (!func->getName().empty() && func->isOperator()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does func->isOperator()
already check for an empty name? In that case you could just use that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't right now but it seems like a reasonable thing to add there.
lib/AST/Decl.cpp
Outdated
ArrayRef<Identifier>()); | ||
|
||
if (afd->getFullName().getKind() == DeclNameKind::Subscript) { | ||
return DeclName::createSubscript(ctx, ArrayRef<Identifier>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't correct; you need the index labels too. Isn't it okay to just continue returning afd->getFullName()
though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why I changed this in the first place. I'll change it back to how it was
lib/Sema/CSGen.cpp
Outdated
@@ -1168,7 +1166,7 @@ namespace { | |||
CS.addBindOverloadConstraint(fnTy, choice, subscriptMemberLocator, | |||
CurDC); | |||
} else { | |||
CS.addValueMemberConstraint(baseTy, Context.Id_subscript, | |||
CS.addValueMemberConstraint(baseTy, DeclName::createSubscript(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this correctly distinguish between subscript
and subscript()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. I don't understand your specific concern though…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the difference between a reference to "any subscript" and "a subscript with zero arguments", or between a base DeclName and a compound DeclName. Just wanted to make sure this was producing a base DeclName and not a compound one.
lib/Sema/CSSimplify.cpp
Outdated
@@ -2795,8 +2795,16 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, | |||
// anything else, because the cost of the general search is so | |||
// high. | |||
if (baseObjTy->isAnyObject() && argumentLabels) { | |||
memberName = DeclName(TC.Context, memberName.getBaseName(), | |||
argumentLabels->Labels); | |||
switch (memberName.getKind()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there should be a general withArgumentLabels
operation on DeclName?
lib/Sema/TypeCheckProtocol.cpp
Outdated
} | ||
} else { | ||
if (lhs.getKind() == rhs.getKind()) { | ||
score = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about argument labels?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't they handled further down (line 5205 - 5210)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, whoops. I thought this was an early return for some reason.
lib/Serialization/ModuleFile.cpp
Outdated
@@ -297,22 +298,36 @@ namespace { | |||
/// Used to deserialize entries in the on-disk decl hash table. | |||
class ModuleFile::DeclTableInfo { | |||
public: | |||
using internal_key_type = StringRef; | |||
using external_key_type = Identifier; | |||
using internal_key_type = std::pair<DeclNameKind, StringRef>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good, use something like this for the ClangImporter lookup tables, please.
// Subscript | ||
return DeclName::createSubscript(); | ||
} else { | ||
// Serialized prior to special DeclNames. Just read the normal identifier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point the module format isn't stable, so we can just make a breaking change here. It seems more important to look forward than backward; in a forward world, we've introduced a new special kind that this compiler treats as a normal name. We could either import it as a normal name or fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I didn't want to be the first one to increase the major version though ;-) But with your OK, I'll go ahead ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're treating the minor version as a breaking version for now, please continue doing that.
Thanks for the review. It explained a few things I didn't quite understand before. 😄 |
Just a friendly ping that it would be great if you could discuss internally if we want to continue with the |
It sounds to me like the base name of a DeclName should use a new DeclBaseName type that's either an identifier or one of these special names. We shouldn't contaminate the entire Identifier type with the possibility of special names, but we also shouldn't pretend that special names only make sense at the DeclName level. |
Sounds very reasonable to me. |
Yeah. That might not be a very fun project, but it's probably the right thing to do. I'd wait for @jrose-apple's blessing before getting started, though. |
I'm sad to have three types, because I'm not sure Identifier will be pulling its weight after this, but it's always easier to collapse back down to two if we change our minds later, and right now we really have identified three different kinds of entities. |
Thanks for weighing in, @rjmccall. |
Cool, I'll try and tackle this one more time then ;-) |
8a08dab
to
2a3e8ad
Compare
The new DeclBaseName type will later abstract over normal Identifiers as they exist now and special names that don't have an identifier in the surface language like subscripts
Special names will in the future represent names that don't have a surface language identifier like subscript. For this, change the backing type of DeclName to a new type DeclBaseName that will later be able to represent special names. ValueDecls may therefore no longer have an identifier name. Thus we move the getName method from ValueDecl to the subtypes that are guaranteed to have identifier names
Special DeclNames represent names that do not have an identifier in the surface language. This implies serializing the information about whether a name is special together with its identifier (if it is not special) in both the module file and the swift lookup table. ValueDecls are not guaranteed to have a name anymore and thus getNameStr is moved from ValueDecl to subclasses that guarantee to be backed by identifiers. Other calls need to handle special names if the context doesn't guarantee that the declaration's name is non-special.
2a3e8ad
to
b5074d3
Compare
Please test with following pull request: @swift-ci Please test |
Build failed |
Build failed |
OK, so I've implemented the changes with a new type I've got one more open point which is that subscripts are currently mangled as something like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does look a lot better. It seems obvious now that we really do have three different kinds of things. Thanks, Alex!
The patch is still pretty big; it would be nice to factor out some of the changes that don't depend on DeclBaseName already existing, so we can review those separately and/or get those in first. I scanned through the entire thing today but that doesn't mean I didn't miss something.
return userFacingStr().compare(other.userFacingStr()); | ||
} | ||
|
||
bool operator==(StringRef Str) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem too easy to misuse. Maybe we should go the Clang route: bool isStr(StringRef str)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What kind of misuse do you have in mind? I quite liked the brevity of ==
in if
s. But I can, of course, change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I was worried about == "subscript"
(and whatever else we add), and maybe that's being too cautious.
|
||
raw_ostream &operator<<(raw_ostream &OS, swift::DeclBaseName D); | ||
|
||
// Identifiers hash just like pointers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: comment.
// Identifiers hash just like pointers. | ||
template<> struct DenseMapInfo<swift::DeclBaseName> { | ||
static swift::DeclBaseName getEmptyKey() { | ||
return swift::DeclBaseName::getEmptyKey(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Why the extra level of indirection? It's not like getEmptyKey
is going to be used anywhere else.
(I see that Identifier did this too, but it's weird there too.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just copying what Identifier
did ;-). I'll change it.
@@ -154,6 +154,8 @@ class Mangler { | |||
void mangleProtocolList(ArrayRef<Type> protocols); | |||
void mangleIdentifier(Identifier ident, | |||
OperatorFixity fixity = OperatorFixity::NotOperator); | |||
void mangleIdentifier(DeclBaseName ident, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we want to do about things like this? Is continuing to say mangleIdentifier
the right way forward? I think it's fine for this patch (minimizes an already large changeset), but what do you think for follow-up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would imagine that we will at some point change the mangling name of subscripts to something that doesn't contain the string "subscript" (I'd use a new special character in the mangling scheme). Then the special names would be handled on the call side of this function and we could remove it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, we could do it based on the DeclKind. Okay, that seems reasonable.
// have an identifier. As above, these IDs must \em not be renumbered or | ||
// reordered without incrementing VERSION_MAJOR. | ||
enum SpecialNameID : uint8_t { | ||
SUBSCRIPT_ID = NUM_SPECIAL_MODULES, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems dangerous. How do people know whether to check NUM_SPECIAL_MODULES or NUM_SPECIAL_IDS? I think we should either fold the two lists together or allow the same value to represent multiple things (builtin module OR subscript
, current module OR init
, etc).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think folding the two enums together would be the right way to go. Didn't really feel too comfortable with it but wanted an opinion on whether using the same enum for both special module names and other special names would be alright.
@@ -0,0 +1,16 @@ | |||
// RUN: %target-run-simple-swift | %FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: execution tests don't belong in test/Sema/. Can you move this to test/Interpreter/ instead?
} | ||
} | ||
|
||
SR2575().subscript() // expected-error{{type 'SR2575' has no member 'subscript'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably make a note / bug to come back and improve this specific case later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you wan to change here? Offer a fixit to change the call to subscript
to a proper subscript?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like that. Maybe something that makes it clear it's looking for functions named subscript rather than actual subscripts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll open a follow-up JIRA once the PR has been landed.
@@ -116,7 +116,7 @@ func testParseErrors3(_ c1: C1) { | |||
|
|||
func testParseErrors4() { | |||
// Subscripts | |||
_ = #selector(C1.subscript) // expected-error{{type 'C1.Type' has no subscript members}} | |||
_ = #selector(C1.subscript) // expected-error{{type 'C1' has no member 'subscript'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are going to want to support syntax like this for getting the selector of a subscript getter or setter some day: #selector(getter:C1.subscript)
. It doesn't currently work but that doesn't mean it shouldn't work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that C1.subscript
will always be invalid unless you declare a func `subscript`() {}
. Otherwise we would get into ambiguity issues if you declare both a subscript and a function named subscript
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what the getter:
is for. Or whatever the no-argument function disambiguation syntax turns out to be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but then I don't get why we want to support syntax like this. #selector(getter:…)
would then need to implement some special lookup logic to handle the subscript case, I'd say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…and I don't quite understand your objection. How do you want to spell "the selector of the getter for this subscript"?
I suppose we haven't had a huge need for it yet, but it's not an unreasonable thing to ask for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my point is that, from my point of view, whether or not we want to support selectors for subscripts is nothing to worry about for now and that we will only need to worry about how to implement it when the time comes.
Or maybe let's ask the other way round: What would you want to be changed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned about the diagnostic. It implies that the compiler is interpreting this syntax as a reference to a member named 'subscript', which would lead to a user thinking they've done something wrong, rather than this simply being a feature that's not implemented yet.
It's not a deal-breaker, but I consider us not supporting subscript selectors to just be a bug, and therefore the compiler shouldn't tell the user they've done something wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, OK, now I see. For now I'd suggest changing the error message to type 'C1' has no member named 'subscript'
(adding the word 'named'). I guess then it's correct since C1
doesn't have a member named subscript but only a subscript-member.
In a subsequent PR we could then maybe improve the diagnostics by looking if the type would have a subscript-member (which was probably meant) and point out that selectors for subscripts are not yet supported. Once we do support them, this would turn into a fixit for the getter
or setter
syntax.
case DeclBaseName::Kind::Subscript: | ||
return "subscript"; | ||
case DeclBaseName::Kind::Normal: | ||
return llvm::StringSwitch<StringRef>(name.getIdentifier().str()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than a StringSwitch, we should just escape anything that's a keyword.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to escape all keywords here. A function named default
should still be output unescaped. I'm basically escaping a new class of keywords here, namely those that represent special names (in which "subscript" is currently the only member)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that makes sense. You wouldn't want to see func func
in the output of this tool. @nkcsgexi, what do you think? (Sorry to pull you in mid-discusson.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for swift-api-digester per se, being consistent is more important than being precise. So this change is fine as long as we always keep subscript escaped; however, this may not help the tool itself much.
@@ -1,4 +1,4 @@ | |||
// RUN: not --crash %target-swift-frontend %s -typecheck | |||
// RUN: not %target-swift-frontend %s -typecheck | |||
// REQUIRES: asserts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please drop the "REQUIRES: asserts" as well. (We're not good about this and often clean them up in large batches, but it's good to get in the habit.)
Thanks for the review. I guess the only way I could split this in some sensible way would be across my different commits. This would mean, we could split it in up to 5 pull requests:
|
That seems like a reasonable way to split it up. If (3) and (4) don't split nicely at least we'd have gotten (1) and (2) out of the way first, so they're not intermingled with everything else. |
I'll create PRs one by one now, splitting (3) and (4) since the commits are already separate. We can always merge them again if we want to. Part 1 -> #8015 |
This is a preparation to use special DeclNames that don't carry identifiers e.g. for subscripts, initializers, and destructors which shall at some point fix SR-2575 as outlined in #5537 and #6305.