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

Implementation-only import checking for conformances #23808

Merged
merged 4 commits into from
Apr 11, 2019

Conversation

jrose-apple
Copy link
Contributor

Continues building on John's #23702 and my #23722 to start checking the use of conformances. These also create a dependency on the implementation module, even if both the type and the protocol are public. As John puts it, a conformance is basically a declaration that we name as part of another declaration.

More rdar://problem/48991061


SubstitutionMap subConformanceSubs =
concreteConf->getSubstitutions(SF.getParentModule());
visitSubstitutionMap(subConformanceSubs);
Copy link
Contributor Author

@jrose-apple jrose-apple Apr 5, 2019

Choose a reason for hiding this comment

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

This recursive bit was tricky; it's to handle cases like

public struct ConditionalGenericStruct<T> {}
extension ConditionalGenericStruct: NormalProto where T: NormalProto {
  public typealias Assoc = Int
}
public func testConditionalGeneric(_: NormalProtoAssocHolder<ConditionalGenericStruct<NormalStruct>>) {}
// expected-error@-1 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}}

Am I doing this right?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you need to visit the associated conformances too; imagine you have:

protocol Q {}
protocol P {
  associatedtype T : Q
}
struct S1 : P {
  typealias T = S2
}

Let's say that S2 : Q is implementation only. I think your current check will miss that. Note that checking associated conformances should subsume your check.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also I'm worried about infinite recursion here. @DougGregor might have a better idea of the 'canonical' way to visit everything you need exactly once.

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 check associated conformances when doing conformance checking; this section is type signature checking. But I didn't think about infinite recursion in either case, so I'll need to pay more attention there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I think this is okay because substitution conformances always bottom out, even though associated conformances do not. And I don't need to check the associated conformances of my associated conformances because you can always check a conformance on its own while assuming the others are valid and then move on to the next one.

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.

I really like the new abstractions here. Thanks for cleaning this up.

bool treatUsableFromInlineAsPublic);
/// Walks a Type to find all NominalTypes, BoundGenericTypes, and
/// TypeAliasTypes.
class TypeDeclFinder : public TypeWalker {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe these should live in their own header file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, if anything the original AccessScopeChecker should just sink into TypeCheckAccess.cpp. It's not reusable after all.

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'll come back and fix this in a separate PR, since it doesn't strictly need to go into 5.1.

/// the generic type.
class SimpleTypeDeclFinder : public TypeDeclFinder {
/// The function to call when a ComponentIdentTypeRepr is seen.
llvm::function_ref<Action(const TypeDecl *)> Callback;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would feel more confident if this was an std::function since then you don't have to be sure to keep the closure alive somewhere else

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:-/ std::function has a definite cost, though, and doesn't inline away nicely. I'd rather keep this as is.


SubstitutionMap subConformanceSubs =
concreteConf->getSubstitutions(SF.getParentModule());
visitSubstitutionMap(subConformanceSubs);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you need to visit the associated conformances too; imagine you have:

protocol Q {}
protocol P {
  associatedtype T : Q
}
struct S1 : P {
  typealias T = S2
}

Let's say that S2 : Q is implementation only. I think your current check will miss that. Note that checking associated conformances should subsume your check.


SubstitutionMap subConformanceSubs =
concreteConf->getSubstitutions(SF.getParentModule());
visitSubstitutionMap(subConformanceSubs);
Copy link
Contributor

Choose a reason for hiding this comment

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

Also I'm worried about infinite recursion here. @DougGregor might have a better idea of the 'canonical' way to visit everything you need exactly once.

Copy link
Contributor

@beccadax beccadax left a comment

Choose a reason for hiding this comment

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

Somewhere between 98 and 99 percent good.

lib/AST/AccessScopeChecker.cpp Outdated Show resolved Hide resolved
//
// We still don't want to do this if we found issues with the TypeRepr,
// though, because that would result in some issues being reported twice.
if (!foundAnyIssues && type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The code might be easier to follow with an early return here.

if (accessScope.isPublic())
return false;

// Is this a stored property in a non-resilent struct or class?
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "resilent" has a typo.

auto *parentNominal = dyn_cast<NominalTypeDecl>(property->getDeclContext());
if (!parentNominal || parentNominal->isResilient())
return true;
return shouldSkipChecking(parentNominal);
Copy link
Contributor

Choose a reason for hiding this comment

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

Recursing here is clever, but I'm not happy with how it makes the code read:

  1. I had to stop and double-check that skipping the recursive call for everything but stored properties was an intentional behavior. I don't think it's a bug, but it looks like a bug.
  2. You don't really need the full generality of recursion because a NominalTypeDecl will always hit one of the first two ifs, so it makes it look like this might recurse up through the ancestor contexts, even though it actually doesn't.

There are a couple of ways you could address the first point, but in light of the second, I'd suggest extracting an isPublicOrUsableFromInline() helper function and calling that instead of recursing:

  static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
    return VD->getFormalAccessScope(nullptr,
                                    /*treatUsableFromInlineAsPublic*/true)
               .isPublic();
  }

  static bool shouldSkipChecking(const ValueDecl *VD) {
    // Is this part of the module's API or ABI?
    if (isPublicOrUsableFromInline(VD))
      return false;

    // Is this a stored property in a non-resilient struct or class which is
    // part of the module's ABI or API?
    auto *property = dyn_cast<VarDecl>(VD);
    if (!property || !property->hasStorage() || property->isStatic())
      return true;
    auto *parentNominal = dyn_cast<NominalTypeDecl>(property->getDeclContext());
    if (!parentNominal || parentNominal->isResilient())
      return true;
    return !isPublicOrUsableFromInline(parentNominal);
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@slavapestov We do keep running into that helper. Do we want an isFormallyABI or something?

if (!parentNominal || parentNominal->isResilient())
return true;
return shouldSkipChecking(parentNominal);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Stray semicolon.

lib/Sema/TypeCheckAccess.cpp Outdated Show resolved Hide resolved
lib/Sema/TypeCheckAccess.cpp Outdated Show resolved Hide resolved
return;
// Only check individual variables if we didn't check an enclosing
// TypedPattern.
if (seenVars.count(theVar) || theVar->isInvalid())
Copy link
Contributor

Choose a reason for hiding this comment

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

It's really unfortunate that Pattern::forEachNode() doesn't let you skip recursing into the children of a node, but I guess using an ASTWalker instead would be even more painful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, and it's honestly not going to be a very deep tree in practice.

public var (testBadTypeTuple1, testBadTypeTuple2): (BadStruct?, BadClass?) = (nil, nil)
// expected-error@-1 {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
// expected-error@-2 {{cannot use 'BadClass' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
public var (testBadTypeTuplePartlyInferred1, testBadTypeTuplePartlyInferred2): (Optional, Optional) = (Optional<Int>.none, Optional<BadStruct>.none) // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
Copy link
Contributor

@beccadax beccadax Apr 5, 2019

Choose a reason for hiding this comment

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

Note: checkTypedPattern() checks the last VarDecl, which is the one with the bad type in this test case.

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 guess I should try some more orders, yeah.

private var computedIsOkay: BadStruct? { return nil } // okay
private static var staticIsOkay: BadStruct? // okay
@usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
}
Copy link
Contributor

@beccadax beccadax Apr 5, 2019

Choose a reason for hiding this comment

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

The other test file includes test cases with structural types, but this one doesn't. Is that okay? I'm not sure what this file is trying to test.

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 file tests the exception for non-frozen structs and classes when library evolution is enabled. It assumes everything works otherwise; it's just testing the frozen/non-frozen distinction.

Copy link
Contributor

Choose a reason for hiding this comment

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

A comment would make that clearer.

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test source compatibility

@jrose-apple
Copy link
Contributor Author

Okay, I think this is ready. I removed a bunch of duplication from the last commit and now things aren't perfect but they're at least not terribly bad.

What test cases am I missing?

Leaving marked as a draft because it still slurps in #23912, so I have to do one more rebase.

I'll do a follow-up PR that harmonizes John's and my terminology, since right now half of the stuff says "exportability" and the other half says "implementation-only" or "implementationOnly"; and another one that renames AccessScopeChecker.h to TypeDeclFinder.h (and pulls the access control walking out) as requested by Slava.

@swift-ci Please test

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test compiler performance

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test source compatibility

@swift-ci
Copy link
Contributor

swift-ci commented Apr 9, 2019

Build failed
Swift Test Linux Platform
Git Sha - 9c60cae4c39c59907168a90aebd7774f640b2eeb

@jrose-apple jrose-apple changed the title [WIP] Implementation-only import checking for conformances Implementation-only import checking for conformances Apr 9, 2019
@jrose-apple jrose-apple requested review from beccadax and slavapestov and removed request for beccadax April 9, 2019 23:31
@jrose-apple
Copy link
Contributor Author

@swift-ci Please test compiler performance

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 9c60cae4c39c59907168a90aebd7774f640b2eeb

@jrose-apple
Copy link
Contributor Author

Failed in, um, the very thing I'm testing. Maybe I accidentally lost some changes when rebasing?

@jrose-apple
Copy link
Contributor Author

Compiler perf results

Summary for master full

Unexpected test results, excluded stats for ProcedureKit, Tagged, Wordy, Deferred

Regressions found (see below)

Debug-batch

debug-batch brief

Regressed (0)
name old new delta delta_pct
Improved (2)
name old new delta delta_pct
Frontend.NumInstructionsExecuted 29,895,438,266,079 29,525,082,392,802 -370,355,873,277 -1.24% ✅
time.swift-driver.wall 2804.4s 2749.5s -54.8s -1.95% ✅
Unchanged (delta < 1.0% or delta < 100.0ms) (1)
name old new delta delta_pct
LLVM.NumLLVMBytesOutput 1,114,053,216 1,114,053,366 150 0.0%

debug-batch detailed

Regressed (1)
name old new delta delta_pct
AST.NumSourceLinesPerSecond 2,251,754 2,297,279 45,525 2.02% ⛔
Improved (6)
name old new delta delta_pct
Frontend.NumInstructionsExecuted 29,895,438,266,079 29,525,082,392,802 -370,355,873,277 -1.24% ✅
Sema.NumConstraintScopes 18,011,511 17,540,127 -471,384 -2.62% ✅
Sema.NumConstraintsConsideredForEdgeContraction 45,554,258 43,388,452 -2,165,806 -4.75% ✅
Sema.NumLeafScopes 12,083,326 11,784,684 -298,642 -2.47% ✅
Sema.OverriddenDeclsRequest 8,060,968 7,959,067 -101,901 -1.26% ✅
Sema.USRGenerationRequest 13,133,931 12,922,649 -211,282 -1.61% ✅
Unchanged (delta < 1.0% or delta < 100.0ms) (93)
name old new delta delta_pct
AST.NumASTBytesAllocated 71,639,257,132 71,029,843,000 -609,414,132 -0.85%
AST.NumDecls 92,845 92,845 0 0.0%
AST.NumDependencies 212,128 212,130 2 0.0%
AST.NumImportedExternalDefinitions 1,199,076 1,199,076 0 0.0%
AST.NumInfixOperators 34,255 34,255 0 0.0%
AST.NumLinkLibraries 0 0 0 0.0%
AST.NumLoadedModules 253,056 253,056 0 0.0%
AST.NumLocalTypeDecls 123 123 0 0.0%
AST.NumObjCMethods 15,429 15,429 0 0.0%
AST.NumPostfixOperators 18 18 0 0.0%
AST.NumPrecedenceGroups 16,924 16,924 0 0.0%
AST.NumPrefixOperators 85 85 0 0.0%
AST.NumReferencedDynamicNames 122 122 0 0.0%
AST.NumReferencedMemberNames 4,042,754 4,042,754 0 0.0%
AST.NumReferencedTopLevelNames 313,132 313,132 0 0.0%
AST.NumSourceBuffers 387,344 387,344 0 0.0%
AST.NumSourceLines 2,973,052 2,973,052 0 0.0%
AST.NumTotalClangImportedEntities 4,579,567 4,569,384 -10,183 -0.22%
AST.NumUsedConformances 260,546 260,546 0 0.0%
Driver.ChildrenMaxRSS 110,494,564,352 109,887,848,448 -606,715,904 -0.55%
Driver.DriverDepCascadingDynamic 0 0 0 0.0%
Driver.DriverDepCascadingExternal 0 0 0 0.0%
Driver.DriverDepCascadingMember 0 0 0 0.0%
Driver.DriverDepCascadingNominal 0 0 0 0.0%
Driver.DriverDepCascadingTopLevel 0 0 0 0.0%
Driver.DriverDepDynamic 0 0 0 0.0%
Driver.DriverDepExternal 0 0 0 0.0%
Driver.DriverDepMember 0 0 0 0.0%
Driver.DriverDepNominal 0 0 0 0.0%
Driver.DriverDepTopLevel 0 0 0 0.0%
Driver.NumDriverJobsRun 18,540 18,540 0 0.0%
Driver.NumDriverJobsSkipped 0 0 0 0.0%
Driver.NumDriverPipePolls 199,666 201,467 1,801 0.9%
Driver.NumDriverPipeReads 222,529 224,543 2,014 0.91%
Driver.NumProcessFailures 0 0 0 0.0%
Frontend.MaxMallocUsage 729,673,511,696 725,924,788,872 -3,748,722,824 -0.51%
Frontend.NumProcessFailures 0 0 0 0.0%
IRModule.NumIRAliases 115,668 115,668 0 0.0%
IRModule.NumIRBasicBlocks 4,387,012 4,387,012 0 0.0%
IRModule.NumIRComdatSymbols 0 0 0 0.0%
IRModule.NumIRFunctions 2,083,997 2,083,997 0 0.0%
IRModule.NumIRGlobals 2,104,798 2,104,798 0 0.0%
IRModule.NumIRIFuncs 0 0 0 0.0%
IRModule.NumIRInsts 54,813,513 54,813,513 0 0.0%
IRModule.NumIRNamedMetaData 90,002 90,002 0 0.0%
IRModule.NumIRValueSymbols 3,787,221 3,787,221 0 0.0%
LLVM.NumLLVMBytesOutput 1,114,053,216 1,114,053,366 150 0.0%
Parse.NumFunctionsParsed 168,408 168,408 0 0.0%
Parse.NumIterableDeclContextParsed 1,163,266 1,163,266 0 0.0%
SILModule.NumSILGenDefaultWitnessTables 0 0 0 0.0%
SILModule.NumSILGenFunctions 1,071,771 1,071,771 0 0.0%
SILModule.NumSILGenGlobalVariables 40,103 40,103 0 0.0%
SILModule.NumSILGenVtables 11,745 11,745 0 0.0%
SILModule.NumSILGenWitnessTables 45,902 45,902 0 0.0%
SILModule.NumSILOptDefaultWitnessTables 0 0 0 0.0%
SILModule.NumSILOptFunctions 1,502,340 1,502,340 0 0.0%
SILModule.NumSILOptGlobalVariables 40,984 40,984 0 0.0%
SILModule.NumSILOptVtables 19,254 19,254 0 0.0%
SILModule.NumSILOptWitnessTables 99,775 99,775 0 0.0%
Sema.AccessLevelRequest 2,768,880 2,766,417 -2,463 -0.09%
Sema.CustomAttrNominalRequest 0 0 0 0.0%
Sema.DefaultAndMaxAccessLevelRequest 62,984 62,976 -8 -0.01%
Sema.DefaultTypeRequest 370,154 370,154 0 0.0%
Sema.EnumRawTypeRequest 20,134 20,134 0 0.0%
Sema.ExtendedNominalRequest 4,060,275 4,052,009 -8,266 -0.2%
Sema.InheritedDeclsReferencedRequest 4,751,981 4,735,432 -16,549 -0.35%
Sema.InheritedTypeRequest 646,215 646,286 71 0.01%
Sema.IsDynamicRequest 2,134,078 2,134,078 0 0.0%
Sema.IsObjCRequest 1,874,699 1,873,868 -831 -0.04%
Sema.MangleLocalTypeDeclRequest 246 246 0 0.0%
Sema.NamedLazyMemberLoadFailureCount 23,284 23,232 -52 -0.22%
Sema.NamedLazyMemberLoadSuccessCount 19,496,042 19,498,938 2,896 0.01%
Sema.NominalTypeLookupDirectCount 33,676,983 33,558,979 -118,004 -0.35%
Sema.NumConformancesDeserialized 6,905,659 6,870,966 -34,693 -0.5%
Sema.NumDeclsDeserialized 51,165,120 50,869,830 -295,290 -0.58%
Sema.NumDeclsFinalized 1,937,417 1,937,417 0 0.0%
Sema.NumDeclsTypechecked 1,003,408 1,003,408 0 0.0%
Sema.NumDeclsValidated 2,350,955 2,350,955 0 0.0%
Sema.NumFunctionsTypechecked 1,063,532 1,063,532 0 0.0%
Sema.NumGenericSignatureBuilders 1,289,802 1,286,472 -3,330 -0.26%
Sema.NumLazyGenericEnvironments 10,308,768 10,255,248 -53,520 -0.52%
Sema.NumLazyGenericEnvironmentsLoaded 222,751 222,779 28 0.01%
Sema.NumLazyIterableDeclContexts 7,153,492 7,142,149 -11,343 -0.16%
Sema.NumTypesDeserialized 17,241,293 17,177,046 -64,247 -0.37%
Sema.NumTypesValidated 1,795,698 1,795,698 0 0.0%
Sema.NumUnloadedLazyIterableDeclContexts 4,699,067 4,702,954 3,887 0.08%
Sema.RequirementRequest 71,867 71,867 0 0.0%
Sema.SelfBoundsFromWhereClauseRequest 6,815,966 6,792,516 -23,450 -0.34%
Sema.SetterAccessLevelRequest 157,241 157,241 0 0.0%
Sema.SuperclassDeclRequest 86,208 86,246 38 0.04%
Sema.SuperclassTypeRequest 37,133 37,133 0 0.0%
Sema.TypeDeclsFromWhereClauseRequest 33,250 33,242 -8 -0.02%
Sema.UnderlyingTypeDeclsReferencedRequest 185,120 184,873 -247 -0.13%

Release

release brief

Regressed (0)
name old new delta delta_pct
Improved (0)
name old new delta delta_pct
Unchanged (delta < 1.0% or delta < 100.0ms) (3)
name old new delta delta_pct
Frontend.NumInstructionsExecuted 29,063,512,320,894 29,010,625,697,099 -52,886,623,795 -0.18%
LLVM.NumLLVMBytesOutput 946,121,674 946,128,714 7,040 0.0%
time.swift-driver.wall 5084.9s 5085.1s 203.7ms 0.0%

release detailed

Regressed (0)
name old new delta delta_pct
Improved (1)
name old new delta delta_pct
Sema.NumConstraintScopes 16,161,421 15,689,803 -471,618 -2.92% ✅
Unchanged (delta < 1.0% or delta < 100.0ms) (22)
name old new delta delta_pct
AST.NumImportedExternalDefinitions 230,987 230,987 0 0.0%
AST.NumLoadedModules 16,804 16,804 0 0.0%
AST.NumTotalClangImportedEntities 792,872 792,872 0 0.0%
AST.NumUsedConformances 261,462 261,462 0 0.0%
IRModule.NumIRBasicBlocks 3,844,580 3,844,580 0 0.0%
IRModule.NumIRFunctions 1,754,103 1,754,103 0 0.0%
IRModule.NumIRGlobals 1,856,775 1,856,775 0 0.0%
IRModule.NumIRInsts 34,649,265 34,649,265 0 0.0%
IRModule.NumIRValueSymbols 3,367,396 3,367,396 0 0.0%
LLVM.NumLLVMBytesOutput 946,121,674 946,128,714 7,040 0.0%
SILModule.NumSILGenFunctions 743,986 743,986 0 0.0%
SILModule.NumSILOptFunctions 1,001,526 1,001,526 0 0.0%
Sema.NumConformancesDeserialized 2,334,731 2,334,655 -76 -0.0%
Sema.NumDeclsDeserialized 6,257,924 6,255,677 -2,247 -0.04%
Sema.NumDeclsValidated 1,208,498 1,208,498 0 0.0%
Sema.NumFunctionsTypechecked 479,126 479,126 0 0.0%
Sema.NumGenericSignatureBuilders 210,562 210,376 -186 -0.09%
Sema.NumLazyGenericEnvironments 1,290,079 1,289,512 -567 -0.04%
Sema.NumLazyGenericEnvironmentsLoaded 22,436 22,436 0 0.0%
Sema.NumLazyIterableDeclContexts 795,511 795,504 -7 -0.0%
Sema.NumTypesDeserialized 3,330,517 3,329,434 -1,083 -0.03%
Sema.NumTypesValidated 734,602 734,602 0 0.0%

@brentdax, any idea why it's failing to post the comment? It says the build failed but I can't see how it failed, and at the same time the results look strange again.

These also create a dependency on the implementation module, even if
both the type and the protocol are public. As John puts it, a
conformance is basically a declaration that we name as part of another
declaration.

More rdar://problem/48991061
Okay, strictly we're checking the "signature conformances" of a newly-
declared conformance, but that wouldn't have fit on one line. This is
making sure that we don't have a requirement on an associated type (or
on the conforming type) that can only be satisfied using a conformance
in an implementation-only import.
This will be used in the next commit for checking conformance use in
inlinable function bodies, but for now it's No Functionality Change.
This includes both the types and the values (generic functions, etc)
used in the inlinable code. We get some effectively duplicate
diagnostics at this point because of this, but we can deal with that
at a future date.

Last part of rdar://problem/48991061
@jrose-apple
Copy link
Contributor Author

@swift-ci Please test

@jrose-apple jrose-apple marked this pull request as ready for review April 10, 2019 18:12
@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 1d0c380892d5dce23642ca226b21735de59ce0f2

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 1d0c380892d5dce23642ca226b21735de59ce0f2

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test macOS

}
extension PublicInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
extension PublicInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
extension PublicInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay
Copy link
Contributor

Choose a reason for hiding this comment

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

What error would we emit for something like this?

public struct PublicExplicitAssociatedTypeImpl {
  public typealias Assoc = NormalStruct
  public func takesAssoc(_: Assoc) {}
}
extension PublicExplicitAssociatedTypeImpl: PublicInferredAssociatedType {}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, should look the same, but worth adding as a test case. Thanks!

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.

Looks good! Optional suggestions follow.

// for the type, and therefore we could skip this extra work?
if (!foundAnyIssues && type) {
type.walk(SimpleTypeDeclFinder([&](const TypeDecl *typeDecl) {
// Note that if we have a type, we can't skip checking it even if the
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory we could also skip walking the type if our source file doesn't import anything as implementation-only, but that might be too much conditionalizing for little gain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, John had his part skipping. Preliminary "test compiler performance" didn't show any significant difference, but it's probably worth following up here to make sure that really is the case.

}

Action visitNominalType(NominalType *ty) override {
visitTypeDecl(ty->getDecl());
Copy link
Contributor

Choose a reason for hiding this comment

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

@AnthonyLatsis's PR adds where clauses for types without generic parameter lists, so you should just duplicate the bound generic type logic here; the nominal type might add a conformance that's not there on the parent type.

We could also skip visiting the parent type as an optimization but I don't know how.

(I'd really like to merge NominalType/BoundGenericType into a single type, remove the parent type concept altogether, and store the SubstitutionMap directly inside the 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.

Uh, hm. Nominal types don't carry that information, so unless the current DeclContext is now relevant I don't see how the current logic will work with that. Are you sure that's something that can come up in this part of the code? (as opposed to being checked at the FuncDecl or whatever when we walk the requirements)

@@ -0,0 +1,263 @@
// RUN: %empty-directory(%t)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add some tests where a base class has an implementation-only conformance to a protocol, and the derived class appears in a substitution map somewhere against a conformance requirement to the protocol? This is where you get an InheritedProtocolConformance and we should make sure that works too.

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 I had them in the other file (the non-inlinable tests) but I'll add some here too.

@jrose-apple
Copy link
Contributor Author

I'm going to merge this as is so I can start PR testing for the 5.1 branch, but I'll make these additional changes along with the general follow-up cleanup work.

@jrose-apple jrose-apple merged commit 6ea773e into swiftlang:master Apr 11, 2019
@jrose-apple jrose-apple deleted the conformance-conformance branch April 11, 2019 15:50
jrose-apple added a commit to jrose-apple/swift that referenced this pull request Apr 11, 2019
…rmance

Implementation-only import checking for conformances

(cherry picked from commit 6ea773e)
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.

4 participants