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

Projects
None yet
4 participants
@jrose-apple
Copy link
Member

jrose-apple commented Apr 5, 2019

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

@jrose-apple jrose-apple requested review from slavapestov, rjmccall and brentdax Apr 5, 2019


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

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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?

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

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.

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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.

@slavapestov
Copy link
Member

slavapestov left a comment

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 {

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

Maybe these should live in their own header file?

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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;

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

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

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

:-/ 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);

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

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);

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 5, 2019

Member

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.

@brentdax
Copy link
Collaborator

brentdax left a comment

Somewhere between 98 and 99 percent good.

Show resolved Hide resolved lib/AST/AccessScopeChecker.cpp Outdated
//
// 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) {

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

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?

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

Nit: "resilent" has a typo.

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

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

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);
  }

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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

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

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

Nit: Stray semicolon.

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

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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'}}

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

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

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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'}}
}

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 5, 2019

Author Member

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.

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 5, 2019

Collaborator

A comment would make that clearer.

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 9, 2019

@swift-ci Please test source compatibility

@jrose-apple jrose-apple force-pushed the jrose-apple:conformance-conformance branch from 9c60cae to 1d0c380 Apr 9, 2019

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 9, 2019

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

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 9, 2019

@swift-ci Please test compiler performance

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 9, 2019

@swift-ci Please test source compatibility

@swift-ci

This comment has been minimized.

Copy link
Contributor

swift-ci commented Apr 9, 2019

Build failed
Swift Test Linux Platform
Git Sha - 9c60cae

@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 brentdax and slavapestov and removed request for brentdax Apr 9, 2019

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

@swift-ci Please test compiler performance

@swift-ci

This comment has been minimized.

Copy link
Contributor

swift-ci commented Apr 10, 2019

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

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

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

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

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.

jrose-apple added some commits Apr 5, 2019

Check signatures for conformances from implementation-only imports
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
Check assoc types for conformances from implementation-only imports
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.
Pass a ConcreteDeclRef through for availability checking where present
This will be used in the next commit for checking conformance use in
inlinable function bodies, but for now it's No Functionality Change.
Check for use of implementation-only conformances in inlinable code
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 jrose-apple force-pushed the jrose-apple:conformance-conformance branch from 1d0c380 to 7006aa0 Apr 10, 2019

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

@swift-ci Please test

@jrose-apple jrose-apple marked this pull request as ready for review Apr 10, 2019

@swift-ci

This comment has been minimized.

Copy link
Contributor

swift-ci commented Apr 10, 2019

Build failed
Swift Test Linux Platform
Git Sha - 1d0c380

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

@swift-ci Please test

@swift-ci

This comment has been minimized.

Copy link
Contributor

swift-ci commented Apr 10, 2019

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

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 10, 2019

@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

This comment has been minimized.

Copy link
@brentdax

brentdax Apr 10, 2019

Collaborator

What error would we emit for something like this?

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

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 11, 2019

Author Member

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

@slavapestov
Copy link
Member

slavapestov left a comment

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

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 11, 2019

Member

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 11, 2019

Author Member

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());

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 11, 2019

Member

@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...)

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 11, 2019

Author Member

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)

This comment has been minimized.

Copy link
@slavapestov

slavapestov Apr 11, 2019

Member

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.

This comment has been minimized.

Copy link
@jrose-apple

jrose-apple Apr 11, 2019

Author Member

I think I had them in the other file (the non-inlinable tests) but I'll add some here too.

@jrose-apple

This comment has been minimized.

Copy link
Member Author

jrose-apple commented Apr 11, 2019

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 apple:master Apr 11, 2019

4 checks passed

Swift Test Linux Platform No test results found.
Details
Swift Test Linux Platform (smoke test)
Details
Swift Test OS X Platform No test results found.
Details
Swift Test OS X Platform (smoke test)
Details

@jrose-apple jrose-apple deleted the jrose-apple:conformance-conformance branch Apr 11, 2019

jrose-apple added a commit to jrose-apple/swift that referenced this pull request Apr 11, 2019

Merge pull request apple#23808 from jrose-apple/conformance-conformance
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
You can’t perform that action at this time.