-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[CodeCompletion] Duplicate existential requirements when restated #16811
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
[CodeCompletion] Duplicate existential requirements when restated #16811
Conversation
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 happens if one requirement is overloaded and the only difference between the overloads is the generic signatures? I think calling getResult() on the overload signature will produce the same type then. Eg,
func f<T : P>(_: T)
func f<T : Q>(_: T)
lib/AST/LookupVisibleDecls.cpp
Outdated
if (foundFuncs[{VD->getFullName(), type}].empty()) { | ||
parentConsumer.foundDecl(VD, Reason); | ||
} | ||
foundFuncs[{VD->getFullName(), type}].insert(VD); |
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 return value of insert() tells you if the entry was inserted or not so you can use that instead.
Why is the value of this map a set of types here, do you actually do anything with them?
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.
No, that's the key type, it's just that it's a pair (I'll change it to a pair, right now it's a tuple). I need both a type and a declName to sort out functions and 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.
The return value of insert() tells you if the entry was inserted or not so you can use that instead.
Actually, don't need more that one element per key, != empty
suffices, so I better change the container type to TinyVector
or simply a pointer.
lib/AST/LookupVisibleDecls.cpp
Outdated
|
||
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { | ||
assert(VD); | ||
if (!VD->getDeclContext()->getAsProtocolOrProtocolExtensionContext()) { |
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 understand why this is specific to protocol extensions
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 isn't! Thanks for the remark, I'll change this to dyn_cast<ProtocolDecl>
lib/AST/LookupVisibleDecls.cpp
Outdated
return; | ||
} | ||
auto type = | ||
VD->getOverloadSignatureType()->getAs<AnyFunctionType>()->getResult(); |
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.
getAs returns null if it’s the wrong type; use castTo() if you don’t expect it to fail
lib/AST/LookupVisibleDecls.cpp
Outdated
getReasonForSuper(Reason), TypeResolver, GSB, Visited); | ||
|
||
if (auto superclass = Archetype->getSuperclass()) | ||
if (ArchetypeType *AT = BaseTy->getAs<ArchetypeType>()) { |
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 a non-functional change? Can you split it off into a separate patch if it’s unrelated?
Using isa is always better than checking the kind directly. I’m not sure what you mean with regard to wiping out Self requirements - can you give an example? |
I checked for that and surprisingly, it works. I will add a test case. This must be due to the information UDP After rechecking, it seems you're right after all. It works without getting the overload signature type, which I do get. |
Thank you for the advice. So it is safer, but less efficient.
The type of
That said, and considering subscripts have to be handled specially since they are not self-curried with the generic signature still floating around, ideally, to tell apart restatements we have to compare with
Additionally, since the type of an associated type is also dependent on |
Types are compared for pointer equality because they are memoized. We never allocate the same type twice. However, type aliases and other sugar do create unique types, so you should always use isEqual() (or compare CanTypes) to strip off the sugar before. I think the reason the example with overloads by generic signature worked is because it was comparing sugared types. Each generic parameter type gets a sugared type with its name as written in source, but when canonicalized, only the index in the signature matters, so I think it will stop working once you use canonical types instead. Eg, we want these two to be the same decl:
|
Thank you for the explanation. I do compute new signatures and create new types here for comparison reasons, but they still somehow manage to correctly compare if they are canonical.
I faced an opposite problem when fixing the overload issue: two signatures, for example <Self> (Self) -> () -> ()
<Self> (Self) -> () -> () compare as different if I do not canonicalize them and equal otherwise. Is this due to each |
I now remove the requirements from Also simplified the container types. |
a42ea6e
to
3db6673
Compare
3db6673
to
6c13eba
Compare
Oops, conflicts from #16534 |
lib/AST/LookupVisibleDecls.cpp
Outdated
if (params.size() == 1 && !isa<SubscriptDecl>(VD)) { | ||
type = AFT->getResult()->getCanonicalType(); | ||
} else { | ||
auto newReqs = sig->getRequirements().drop_front(); |
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 don’t make assumptions about the order of requirements in the generic signature like this. There must be a better way to accomplish what you’re trying to do. Perhaps you want to drop any conformance requirement with Self on the left hand side instead of assuming it’s the first one. Also this kind of thing should be factored out into a new method; the code here is getting a bit too long.
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 you want to drop any conformance requirement with Self on the left hand side instead of assuming it’s the first one.
OK, fair enough. Who knows how the order might change in the future.
But... ~30 lines is long? :)
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 will be longer than 30 lines once you do the right thing here, and it's more about single responsibility per method than raw length.
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 see, you mean the type-computing logic.
Yes, each protocol has its own sugared Self type, but they all canonicalize to the same thing. |
lib/AST/LookupVisibleDecls.cpp
Outdated
if (params.size() == 1 && !isa<SubscriptDecl>(VD)) { | ||
return AFT->getResult()->getCanonicalType(); | ||
} | ||
GenericTypeParamType *SelfParam; |
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.
You don't have to "search" for it. Instead use VD->getDeclContext()->getSelfInterfaceType()
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, that is exactly what I was looking for...in the wrong place.
lib/AST/LookupVisibleDecls.cpp
Outdated
|
||
return GenericFunctionType::get(newSig, AFT->getInput(), | ||
AFT->getResult(), AFT->getExtInfo()) | ||
->getCanonicalType(newSig); |
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.
Indentation needs to be fixed, and you can use the no-argument form of getCanonicalType() instead.
lib/AST/LookupVisibleDecls.cpp
Outdated
private: | ||
CanType stripSelfRequirementsIfNeeded(ValueDecl *VD, AnyFunctionType *AFT) { | ||
// Preserve the generic signature if this is a subscript, which are uncurried, | ||
// or if we have genenic params other than Self. Otherwise, strip if off |
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.
typo: "genenic"
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.
typo: "strip if off"
lib/AST/LookupVisibleDecls.cpp
Outdated
// When preserving the generic signature, we remove the requirements | ||
// from Self to make they don't prevent us from recognizing restatements. | ||
|
||
// This can't be null since we are dealing with method or |
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.
In that case, the function should take a GenericFunctionType instead of an AnyFunctionType, then this comment becomes unnecessary
@slavapestov, have a couple of minutes to take a glance? |
I have the similar SR-7758 waiting which is dependent on the new consumer here. Can I commit to this PR then to solve them together? |
@slavapestov Any comments? Should I ask Ben or someone else to have a look as well? |
cc @nkcsgexi @benlangmuir Please have a look when possible. |
// TEST_INTERNAL_DE-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}} | ||
// TEST_INTERNAL_DE-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}} | ||
|
||
// TEST_PUBLIC_DE: Begin completions, 4 items | ||
// TEST_PUBLIC_DE-DAG: Decl[InstanceMethod]/Super: public func colliding() {|}{{; name=.+$}} | ||
// TEST_PUBLIC_DE-DAG: Decl[InstanceMethod]/Super: public func collidingGeneric<T>(x: T) {|}{{; name=.+$}} |
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.
Why did we lose the public keyword 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.
It was a bit strange but I thought it isn't a big deal since we didn't lose it on the other test. Now I see. Apparently because I am filtering without taking access levels into account. This means that, given
public protocol PublicProtocol {func foo}
protocol InternalProtocol {func foo}
, completing in class Foo: PublicProtocol, InternalProtocol
and class Foo: InternalProtocol, PublicProtocol
will result in the removal of different foo
s from the look up.
Thanks for pointing it out, I'll fix this by prioritizing declarations with higher access level.
@swift-ci Please smoke test |
I guess these are failures from the recently merged removal of |
The failures were because some tests began having unstable result ordering and the |
@AnthonyLatsis Yes, order stability is important. Please use MapVector and similar structures to ensure it doesn't depend on hash values. |
Order restored |
fe33730
to
ef11b11
Compare
@swift-ci Please smoke test |
I got it, let's try again. |
@slavapestov Can you smoke test please? |
@swift-ci Please smoke test |
Resolves SR-7035.
Also resolves duplicates in cases like
Please look into how I tell apart restated requirements. If there is a better way (ideally wipe out any
self
occurences from a type, which isn't trivial as far as I see), I'll be glad to hear.I would like to know if it's more efficient and better to do
than
isa<AbstractFunctionDecl>(decl)
.P.S. There is something strange going on with the
SemanticContextKind
on the tests. Have a look, it can beSuper
orCurrNominal
depending on whether the function argument is or isn't of an archetype type. This was already present, the tests simply checked for one of them among the duplicates.