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

TypeResolution: Abolish TypeResolutionStage::Contextual #39652

Merged
merged 14 commits into from
Nov 29, 2021

Conversation

AnthonyLatsis
Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis commented Oct 8, 2021

The correctness issue that lead to this refactoring is an inconsistency in how generic metatypes are resolved in properties that are referenced and declared under a certain kind of same-type constraint:

protocol P {}
struct Foo<T> {
  static var property1: T.Type { T.self }
}
extension Foo where T == P {
  // error: cannot convert return expression of type 'P.Protocol' to return type 'P.Type'
  static var property2: T.Type { property1 } 
}

This is a source-breaking change, since these generic metatypes would no longer resolve to existential metatypes when a same-type constraint binds the 'instance type' type parameter to an existential type, be it a protocol requirement or other member.


Extracted from #39492

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

This looks pretty reasonable, just a few comments I'd like to hear your thoughts on before we merge this.

SourceLoc loc,
ArrayRef<Type> genericArgs) const;
static Type
applyUnboundGenericArguments(GenericTypeDecl *decl, Type parentTy,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be cleaner if it was an instance member?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Perhaps. The fact that we have to consider passing function objects for opening placeholders and unbound generics just to call applyUnboundGenericArguments, even though it never needs them, was a bit of an eyesore. On the other hand, it interacts with a lot of TypeResolution state, and it is a lot easier to manage on-demand use of the generic environment with an instance member.

Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Oct 18, 2021

Choose a reason for hiding this comment

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

On second thought, I have to object here. It does appear to be visually cleaner the instance member way, but the fact that we sometimes pass a contextual parent type to applyUnboundGenericArguments just doesn't play well together with a type resolution stage, now that it cannot be "contextual". It seems to be more about whether we want to check requirements or not, rather than which stage we're on. Looking forward to further thoughts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@slavapestov Just in case – I'm happy to set this discussion aside.

@@ -3976,9 +3976,13 @@ class ConstraintSystem {
/// parent type by introducing fresh type variables for generic parameters
/// and constructing a bound generic type from these type variables.
///
/// \param useArchetypes When \p decl has no parent, indicates whether to use
/// archetypes for generic parameters of its parent context.
Copy link
Contributor

Choose a reason for hiding this comment

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

Even if there is a parent type, it might be nested inside of another non-type generic context, eg

func foo<T>() {
  struct Outer {
    struct Inner {
      typealias A = Array<T>
  }
}

Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Oct 16, 2021

Choose a reason for hiding this comment

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

IIRC we don't support type declarations nested inside generic (or contextually generic) functions, so it's alright for now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've changed the flag name to "isTypeResolution" to be more explicit.

@AnthonyLatsis AnthonyLatsis force-pushed the no-context branch 3 times, most recently from 12880d4 to 311f321 Compare October 16, 2021 16:13
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please test source compatibility release

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please test source compatibility release

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

Only one remaining question.

/// \returns \c true on success.
bool checkContextualRequirements(
GenericTypeDecl *decl, Type parentTy, SourceLoc loc, ModuleDecl *module,
llvm::function_ref<const GenericEnvironment *()> getGenericEnvironment);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead, can you pass a GenericSignature and call getGenericEnvironment() on it when needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, great suggestion!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Passing a GenericSignature instead of a function ref results in one more duplicate circularity error in compiler_crashers_2_fixed/0161-sr6569.swift. Do you reckon that sounds fine? All this fuss around checkContextualRequirements() is really only to make sure that is works correctly with SIL code, where we explicitly pass a generic environment to type resolution, because it cannot be derived from context.

@AnthonyLatsis AnthonyLatsis force-pushed the no-context branch 2 times, most recently from 8e3d7ff to 0593c29 Compare November 8, 2021 17:22
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@AnthonyLatsis AnthonyLatsis force-pushed the no-context branch 2 times, most recently from 1c606e6 to e223e52 Compare November 9, 2021 18:35
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please test source compatibility

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@AnthonyLatsis AnthonyLatsis merged commit 9fc633e into swiftlang:main Nov 29, 2021
@AnthonyLatsis AnthonyLatsis deleted the no-context branch November 29, 2021 13:44
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.

2 participants