Skip to content

[IDE] Teach type checker about conditional conformance extensions. #14554

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

Merged
merged 2 commits into from
Feb 13, 2018

Conversation

huonw
Copy link
Contributor

@huonw huonw commented Feb 12, 2018

Before conditional conformances, the archetypes in conformance
extensions (i.e. extension Foo: SomeProtocol) were equivalent to those
in the type decl, with the same protocol bounds and so on. The code for
printing "synthesized" members relied on this fact. This commit teaches
that code to deal with archetypes in the conditional conformance
extension when required.

Fixes rdar://problem/36553066 and SR-6930.

No point in getting rid of the sugar so early.
@huonw
Copy link
Contributor Author

huonw commented Feb 12, 2018

@DougGregor thoughts on the safety of this for the swift-*-branches? It's kinda huge (but, fortunately, 300 lines of the net 400 increase are from the expected output of a test case), and fiddly. :(

@huonw
Copy link
Contributor Author

huonw commented Feb 12, 2018

@swift-ci please smoke test

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

This is looking great. It's definitely fine for the Swift 5.0 branch. And although it's quite a bit of code churn... the changes are fairly clear and you've made the code fairly defensive, so I think on balance it's safe for the 4.1 branch. Thank you!

if (!Ext->isConstrainedExtension()) {
auto conformanceIsConditional =
Conf && !Conf->getConditionalRequirements().empty();
if (!Ext->isConstrainedExtension() && !conformanceIsConditional) {
Copy link
Member

Choose a reason for hiding this comment

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

It seems to me that one cannot have a conditional conformance without also having a constrained extension?

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 confusing (took me 10 minutes to remember why I had to have this... will add a comment), but they're referring to two different extensions:

protocol Foo {}
extension Foo { ... } // Ext

struct Bar<T> {}
extension Bar : Foo where T: Foo {} // Conf

Copy link
Member

Choose a reason for hiding this comment

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

Oh, of course! Ext is the protocol extension we're considering, and Conf is referring to a nominal type's conditional conformance to the corresponding protocol.


// FIXME: Could do something here
if (Kind == RequirementKind::Layout)
continue;
Copy link
Member

Choose a reason for hiding this comment

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

I know this isn't your new code, but it'd be so nice to at least check for class constraints here. I guess we'll wait for the isApplicable rewrite.

First = First.subst(subMap);
Second = Second.subst(subMap);

if (!First || !Second) {
Copy link
Member

Choose a reason for hiding this comment

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

Random: should we move this safety code out of the if? This particular code path is known to be a bit crashy, and maybe that would help somewhat...

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 think that can probably become Req.subst(subMap) too, but I was keeping everything the same to reduce risk as much as possible, since this patch is large enough. (And similarly for the class constraints.)

Copy link
Member

Choose a reason for hiding this comment

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

Okay.

Printer << "\n";
}
});
// For top-level decls, only constraint extensions are to print;
Copy link
Member

Choose a reason for hiding this comment

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

If you're revising stuff, could you clean up the comment grammar here?

NominalTypeDecl *SynthesizedTargetNTD = nullptr;
if (SynthesizedTarget) {
SynthesizedTargetNTD = SynthesizedTarget.dyn_cast<NominalTypeDecl *>();
if (!SynthesizedTargetNTD)
Copy link
Member

Choose a reason for hiding this comment

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

I'd appreciate some curly braces for the 3-line body here.

DocInfoConsumer &Consumer) {
if (!D || isa<ParamDecl>(D))
return;
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
if (SynthesizedTarget) {
passExtends((const ValueDecl*)SynthesizedTarget, Consumer);
auto Extends = SynthesizedTarget.dyn_cast<NominalTypeDecl *>();
if (!Extends)
Copy link
Member

Choose a reason for hiding this comment

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

You have this same "figure out the nominal type declaration to which SynthesizedTarget refers" pattern in two places; can you pull it out into a function somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, yeah, bit embarrassing that I missed that. I've made TypeOrExtensionDecl a wrapper struct with some convenience methods like getBaseNominal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although, it is in a somewhat clunky place: PrintOptions.h. Any thoughts for where something like that should go? Decl.h seems wrong to me.

Copy link
Member

Choose a reason for hiding this comment

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

I suspect that TypeOrExtensionDecl could be more widely used, if we were to search around for pointer unions similar to the one you're replacing. It's fine to put it in Decl.h

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FWIW, I couldn't find any other NominalTypeDecl, ExtensionDecl pointer unions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ugh, moving it to Decl.h is rather ugly: PrintOptions.h doesn't/can't include that file, so everything has to be indirected...

Optional<BracketOptions> Bracket) override {
// When we start print a synthesized extension, record the target's USR.
llvm::SmallString<64> Buf;
llvm::raw_svector_ostream OS(Buf);
if (!SwiftLangSupport::printUSR(Target, OS)) {
auto TargetNTD = Target.dyn_cast<NominalTypeDecl *>();
if (!TargetNTD)
Copy link
Member

Choose a reason for hiding this comment

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

Another copy of this pattern

if (TargetToAnalyzerMap.count(Target) == 0) {
NominalTypeDecl *TargetNTD = Target.dyn_cast<NominalTypeDecl *>();
if (!TargetNTD)
TargetNTD = Target.get<ExtensionDecl *>()
Copy link
Member

Choose a reason for hiding this comment

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

Another one :)

@huonw huonw force-pushed the doc-conditional-conformances branch from b4cfd93 to da7b9d6 Compare February 13, 2018 00:30
@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please test

2 similar comments
@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please test

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please test

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci test.

1 similar comment
@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci test.

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - da7b9d6fe59b31911aa85963d7e5a77b67efad77

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - da7b9d6fe59b31911aa85963d7e5a77b67efad77

Before conditional conformances, the archetypes in conformance
extensions (i.e. extension Foo: SomeProtocol) were equivalent to those
in the type decl, with the same protocol bounds and so on. The code for
printing "synthesized" members relied on this fact. This commit teaches
that code to deal with archetypes in the conditional conformance
extension when required.

Fixes rdar://problem/36553066 and SR-6930.
@huonw huonw force-pushed the doc-conditional-conformances branch from da7b9d6 to cb60dbe Compare February 13, 2018 06:37
@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please smoke test and merge

3 similar comments
@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please smoke test and merge

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please smoke test and merge

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please smoke test and merge

@huonw
Copy link
Contributor Author

huonw commented Feb 13, 2018

@swift-ci please smoke test

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.

3 participants