-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Attribute @_implements & deriving enum equality not-named == #8735
Attribute @_implements & deriving enum equality not-named == #8735
Conversation
lib/AST/NameLookup.cpp
Outdated
@@ -1067,6 +1068,13 @@ void MemberLookupTable::addMember(Decl *member) { | |||
// Add this declaration to the lookup set under its compound name and simple | |||
// name. | |||
vd->getFullName().addToLookupTable(Lookup, vd); | |||
|
|||
// Add this declaration to the lookup set under any @_implements name as well |
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.
How do you prevent name lookup on concrete types from finding the decl? We only want to see the implements name while doing conformance checks no?
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.
Correct! Forgot to put this on the todo list. Thanks!
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 the right place to hook this up is not inside name lookup, but in lib/Sema/TypeCheckProtocol.cpp, in particular lookupValueWitnesses()
. When you begin conformance checking, maybe walk the members to collect all _implements attributes, building a mapping from requirement to witness inside the conformance. Then lookupValueWitness()
can avoid most of the work by checking the pre-populated map first.
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.
Good suggestion! Fixed.
lib/Parse/ParseDecl.cpp
Outdated
// We parse a ParsedDeclName here just to make sure it's well formed early; we | ||
// store it in the attr as a StringRef and re-parse on use, so as not to leak | ||
// the ParsedDeclName type into Attr.h | ||
ParsedDeclName parsedName = parseDeclName(ImplName.getValue()); |
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.
Perhaps the semantic checking can be done in lib/Sema/TypeCheckAttr.cpp, so you're not simply throwing away the result.
I would also suggest adding some semantic checks to Sema that _implements only mentions requirements of protocols that the type conforms to. Also do we want to allow _implements inside extensions? This might open up some interesting corner cases. |
b2cb231
to
cd53e8b
Compare
Pushed an update that reworks parsing and definition to handle operator names, permits the bare-minimum feature of @implements(Equatable, ==(:_:)). More comments welcome! |
cd53e8b
to
51561f0
Compare
Pushed update to limit the name visibility to witness lookup. |
51561f0
to
f06277d
Compare
Pushed variant that does everything on the todo list, with some new tests. Going to look at synthesizing this for the motivating case now (dependency-edge cutting) but please let me know if there's anything else you want to see for an initial merge. |
f06277d
to
bcd1913
Compare
Pushed change that derives |
@@ -196,7 +196,8 @@ deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { | |||
// case A, B, C | |||
// | |||
// @derived | |||
// func ==(a: SomeEnum<T...>, b: SomeEnum<T...>) -> Bool { | |||
// @_implements(Equatable, ==(_:_:)) | |||
// func _DerivedEnumEquals(a: SomeEnum<T...>, b: SomeEnum<T...>) -> Bool { |
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.
Is this going to look ugly in generated interface printing? Maybe we could special case that to print this as == (or just skip synthesized decls altogether).
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 it's already marked implicit, so most people won't see it. There are just a few tests that try to print implicit declarations as well.
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, it's implicit, no printing by default.
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.
Drive-by thought:
Wouldn't it be neat to have this be consistent with the "__Anonymous_field" and "__Unnamed_union" terminology introduced in #6935? (I guess it'd be something like "__Derived_conformance_enum_equals".)
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.
…on that note, it's not a type, so lowercase would make more sense.
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.
Changed to __derived_enum_equals
after checking with Jordan.
lib/Sema/TypeCheckAttr.cpp
Outdated
ProtoTypeLoc.setType(T); | ||
|
||
// Definite error-types were already diagnosed in resolveType. | ||
if (!T || T->is<ErrorType>()) |
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.
or hasErrorType(), even
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.
(Assuming you mean hasError()
, and to replace the second disjunct with that test, then: fixed)
lib/Sema/TypeCheckProtocol.cpp
Outdated
|
||
// Do an initial check to see if there are any @_implements remappings | ||
// for this requirement. | ||
if (req->isProtocolRequirement() && req->hasName()) { |
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 factor this out into a new lookupValueWitnessViaImplementsAttr() and use early return to avoid the "} cascade of death"
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.
fixed
@@ -16,6 +16,7 @@ | |||
//===----------------------------------------------------------------------===// | |||
#include "swift/Sema/TypeCheckRequest.h" | |||
#include "swift/AST/Decl.h" | |||
#include "swift/AST/TypeRepr.h" |
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.
Is this necessary?
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.
Yep. the call to std::get<0>(getTypeResolutionPayload())->getLoc()
needs it.
This looks great, nice job! |
bcd1913
to
45b5fb1
Compare
Pushed variant addressing recent review comments from Slava. |
@swift-ci please test |
Build failed |
Build failed |
45b5fb1
to
d65027f
Compare
@swift-ci please smoke test |
@@ -267,6 +267,11 @@ SIMPLE_DECL_ATTR(discardableResult, DiscardableResult, | |||
|
|||
SIMPLE_DECL_ATTR(GKInspectable, GKInspectable, OnVar, 66) | |||
|
|||
DECL_ATTR(_implements, Implements, | |||
OnFunc | OnVar | OnSubscript | OnTypeAlias | |||
| NotSerialized | UserInaccessible, |
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.
If the intent is for these declarations to be invisible to normal name lookup (is that the intent?) or hide it from code completion, then you'll probably need to serialize the attribute.
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.
The attribute isn't what triggers either of those things, so we're okay.
@@ -90,6 +90,7 @@ IDENTIFIER_WITH_NAME(NativeClassLayout, "_NativeClass") | |||
// Operators | |||
IDENTIFIER_WITH_NAME(MatchOperator, "~=") | |||
IDENTIFIER_WITH_NAME(EqualsOperator, "==") | |||
IDENTIFIER_WITH_NAME(DerivedEnumEquals, "_DerivedEnumEquals") |
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.
Lowercase _derivedEnumEquals
because it's a function (not a type)?
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.
renamed to __derived_enum_equals per conversation w/ Jordan
@@ -196,7 +196,9 @@ deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { | |||
// case A, B, C | |||
// | |||
// @derived | |||
// func ==(a: SomeEnum<T...>, b: SomeEnum<T...>) -> Bool { | |||
// @_implements(Equatable, ==(_:_:)) | |||
// func __derived_conformance_enum_equals(a: SomeEnum<T...>, |
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.
s/__derived_conformance/__derived/
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.
fixed
d65027f
to
e6027ee
Compare
Pushed variant with fixed name and change to user-availability bit such that code completion never tells the user about |
@swift-ci please test |
Build failed |
Build failed |
@swift-ci please test |
r=dgregor, merging. |
This implements (non-user-facing) attribute @_implements(Protocol, DeclName) that can be used to conform to a protocol requirement using a decl with a different name than the requirement.
This might sound weird, but it has (at least?) two uses we've already hit:
After initial post and review, I've ticked off the following todos:
rdar://31607695
This was originally motivated by 30959593 (operators defeat incrementality), and I've now tacked on to the end a change that @_implements the derived enum == operator in terms of a different function (creatively named
__derived_enum_equals
)