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

Fix typealias used in associatedtype constraint #27987

Closed
wants to merge 2 commits into from

Conversation

zoecarver
Copy link
Contributor

This patch fixes the compiler crash when type typealias are used in associatedtype constraints. For example:

protocol ProtocolA {
    associatedtype T1
}

struct S : ProtocolA {
  typealias T1 = Int
}

protocol ProtocolB: ProtocolA {
    associatedtype T2: ProtocolB where T2.T1 == T3.T1
    associatedtype X
    typealias T3 = S
}

Resolves SR-11639.

Let me know if I put my tests in the right place.

@@ -100,6 +100,9 @@ class GenericSignatureBuilder::ResolvedType {
UnresolvedType getUnresolvedType() const {
return type;
}

/// Retrieve the stored pointer union
llvm::PointerUnion<PotentialArchetype *, Type> getType() { return type; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: You don't need this. You can use getUnresolvedType.

Copy link
Contributor Author

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 will work because I need a way to check if the union is currently holding a Type or a PotentialArchetype *.

Copy link
Contributor

@CodaFi CodaFi Oct 31, 2019

Choose a reason for hiding this comment

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

UnresolvedType is a PointerUnion of those types.

using UnresolvedType = llvm::PointerUnion<PotentialArchetype *, Type>;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, you're right. I was looking at the wrong method.

@@ -3652,6 +3652,9 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass(
resolutionKind,
wantExactPotentialArchetype);
if (!resolvedBase) return resolvedBase;
if (resolvedBase.getType().is<Type>())
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 kind of surprised this works. It would stomp the condition below, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's evaluated before the below condition so, yes.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean, I’m surprised that this doesn’t break code because of that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm also a little surprised this didn't break anything. It should be noted that it's checking the base type, not type. Then, when the condition succeeds, it will fail out to handleUnresolvedRequirement.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is the Type here Null or have an ErrorType in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the bots showed, this does cause issues. I'll look into it in more depth in the morning.

@CodaFi
Copy link
Contributor

CodaFi commented Nov 4, 2019

Let's see what happens

@swift-ci please smoke test

@CodaFi
Copy link
Contributor

CodaFi commented Nov 4, 2019

@swift-ci please test source compatibility

@zoecarver
Copy link
Contributor Author

Thanks @CodaFi!

@@ -3652,8 +3652,11 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass(
wantExactPotentialArchetype);
if (!resolvedBase) return resolvedBase;
// If the base is concrete, so is this member.
if (resolvedBase.getAsConcreteType())
if (resolvedBase.getAsConcreteType()) {
if (type->isTypeParameter())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This still seems like a bit of a hack to me. Generic parameter types are handled above but, it feels like this type parameter type should never make it here (I have no basis for thinking that, though). Could someone with more knowledge of this bit of code chime in, maybe?

cc @jckarter based on git blame

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't recall, sorry. If you can remove it and it still works, then by all means remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No worries. It's not removing anything as much as conditionally returning an unresolved nullptr. It seems to work, though, so it's probably OK.

@zoecarver zoecarver requested a review from CodaFi November 6, 2019 04:07
@zoecarver
Copy link
Contributor Author

@CodaFi I've run some of the relevant tests and verified that it can compile the standard library. Could you trigger smoke test again?

@CodaFi
Copy link
Contributor

CodaFi commented Nov 6, 2019

Aha, I think I see the problem here. We're better about resugaring Types with TypeAliases. The isTypeParameter() predicate doesn't check for that, which would explain how we managed to get a type parameter to slip in as a concrete type. Ignoring this failure will make the crash go away, but the right answer is to patch up all the places that are asking that question to account for the type alias case. Ultimately, I think this code should compile - it seems to in 4.2.

I think @AnthonyLatsis has the more principled fix (or at least the start of one) in #27425

@zoecarver
Copy link
Contributor Author

@CodaFi great! That would explain it. Do you think I should update this patch to fix getAsConcreteType, submit another patch for it, or let it be done in #27425?

@CodaFi
Copy link
Contributor

CodaFi commented Nov 7, 2019

If you can make use of some of the primitives in Anthony's patch to fix this up, that'd be great.

@zoecarver
Copy link
Contributor Author

Sounds good. I'll do that.

@AnthonyLatsis
Copy link
Collaborator

@CodaFi Is equivClass->concreteType always expected to return a fully concrete type – would Concrete<Assoc> be valid output?

@zoecarver
Copy link
Contributor Author

@CodaFi I spent a bit of time trying to implement parts of Anthony's type substitution in this patch but I couldn't get anything working well. Would you mind pointing me in the right direction?

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Nov 8, 2019

Just had a closer look and realized that I recently fixed a similar untracked issue alongside #23489. A cherry-pick confirms it serendipitously fixes this too. The crasher I unpatched is described here, and the fix is 3c87252

The problem was that a non-generic typealias inside a protocol was always resolved as a DependentMemberType at the structural stage over here, so the type-checker had no clue it was actually dealing with a TypeAliasType.

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Nov 8, 2019

@zoecarver Could you cherry-pick your compiler_crashers test on top if I isolate the fix in a new PR?

@zoecarver
Copy link
Contributor Author

@AnthonyLatsis sure. Thanks for all the links and explanation. I'll see if I can use that to fix the issue.

@AnthonyLatsis
Copy link
Collaborator

Was there a misunderstanding? 3c87252 fixes this issue alongside another one I had, so I proposed to isolate the fix together with your test case now that we actually have an independent one (my own test case depends on the feature implemented in the linked PR).

@zoecarver
Copy link
Contributor Author

zoecarver commented Nov 9, 2019

Ah, I see — my misunderstanding. I thought your commit fixed a similar issue for type aliases (and I was going to apply that to associated types). Anyway, I will update this PR with your fix.

Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis left a comment

Choose a reason for hiding this comment

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

Not to pollute the history on master, can you squash this branch to only contain a single commit with the fix?

@zoecarver
Copy link
Contributor Author

Yep, just did.

Uses Anthony's fix (3c87252) to resolve collisions
@CodaFi
Copy link
Contributor

CodaFi commented Nov 9, 2019

@slavapestov Is more familiar with this particular code path than I am. Could you take a look?

@CodaFi CodaFi requested a review from slavapestov November 9, 2019 02:06
@CodaFi
Copy link
Contributor

CodaFi commented Nov 9, 2019

@swift-ci please smoke test

@CodaFi
Copy link
Contributor

CodaFi commented Nov 9, 2019

@swift-ci please test source compatibility

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Nov 9, 2019

@zoecarver The command for the test should not have the not prefix, because it should typecheck successfully. Otherwise the test fails, as you see. But the second test that failed – 0173-circular-generic-type-alias-deserialization – is unfortunately a regression.

@zoecarver
Copy link
Contributor Author

@AnthonyLatsis I spent all morning trying to debug this. I can get one or the other to pass but not both. Any more ideas?

@AnthonyLatsis
Copy link
Collaborator

I am looking into it as well, as far as my bandwidth permits me. This is almost certainly an annoying circularity issue. I came up with numerous new failing test cases, here's one:

protocol P { associatedtype Assoc }

struct G<T> {}

protocol Q {
    associatedtype A
    typealias Alias<R> = G<R>
    associatedtype C: P where C.Assoc == Alias<Int>
}

@zoecarver
Copy link
Contributor Author

Here's an idea (not sure if it's correct yet): In GenericSignatureBuilder::maybeResolveEquivalenceClass when a parameter type is received, it tries to look it up in Impl::PotentialArchetypes. I think that maybe the issue is that the type should never be added to PotentialArchetypes. Then, in maybeResolveEquivalenceClass it will not be able to find it and will return nullptr (like my original solution). I think the update to resolveTypeInContext might have caused PotentialArchetypes not to be updated.

@futurejones
Copy link
Contributor

Can one of the admins verify this patch?

@zoecarver
Copy link
Contributor Author

Closing this patch for now (may re-open at a later date). It may be best to let #27425 take care of this.

@zoecarver zoecarver closed this Feb 10, 2020
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