From bc85234e5b3773ad9e79518e527787364cd9f225 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Thu, 31 Aug 2023 11:42:59 -0700 Subject: [PATCH 1/2] Serialization: Don't serialize conformances that should be skipped. When `-experimental-serialize-external-decls-only` is specified, skip serializing conformances to protocols that should be skipped to avoid unnecessary typechecking. Also, ensure type and value witnesses are resolved lazily during serialization by passing `true` for `useResolver`. Resolves rdar://114799742 --- lib/Serialization/Serialization.cpp | 12 ++++--- test/Inputs/lazy_typecheck.swift | 39 ++++++++++++++++++++--- test/Inputs/lazy_typecheck_client.swift | 8 +++++ test/ModuleInterface/lazy-typecheck.swift | 14 ++++++-- test/TBD/lazy-typecheck.swift | 29 +++++++++++++++-- 5 files changed, 90 insertions(+), 12 deletions(-) diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ecfa41220d4f0..6376f1942224c 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1665,8 +1665,6 @@ void Serializer::writeLocalNormalProtocolConformance( PrettyStackTraceConformance trace("serializing", conformance); - // The conformance must be complete, or we can't serialize it. - assert(conformance->isComplete() || allowCompilerErrors()); assert(ConformancesToSerialize.hasRef(conformance)); auto protocol = conformance->getProtocol(); @@ -1688,7 +1686,7 @@ void Serializer::writeLocalNormalProtocolConformance( data.push_back(addDeclRef(typeDecl, /*allowTypeAliasXRef*/true)); ++numTypeWitnesses; return false; - }); + }, /*useResolver=*/true); conformance->forEachValueWitness([&](ValueDecl *req, Witness witness) { PrettyStackTraceDecl traceValueWitness( @@ -1717,7 +1715,7 @@ void Serializer::writeLocalNormalProtocolConformance( data.push_back(addSubstitutionMapRef(subs)); data.push_back(witness.getEnterIsolation().has_value() ? 1 : 0); - }); + }, /*useResolver=*/true); unsigned abbrCode = DeclTypeAbbrCodes[NormalProtocolConformanceLayout::Code]; @@ -3261,8 +3259,14 @@ class Serializer::DeclSerializer : public DeclVisitor { size_t addConformances(const IterableDeclContext *declContext, ConformanceLookupKind lookupKind, SmallVectorImpl &data) { + // We don't expect to be serializing conformances for skipped decls. + assert(!S.shouldSkipDecl(declContext->getDecl())); + size_t count = 0; for (auto conformance : declContext->getLocalConformances(lookupKind)) { + if (S.shouldSkipDecl(conformance->getProtocol())) + continue; + data.push_back(S.addConformanceRef(conformance)); count++; } diff --git a/test/Inputs/lazy_typecheck.swift b/test/Inputs/lazy_typecheck.swift index f3b5b89d2654c..7e1dc764b7942 100644 --- a/test/Inputs/lazy_typecheck.swift +++ b/test/Inputs/lazy_typecheck.swift @@ -50,12 +50,12 @@ public func publicFuncWithOpaqueReturnType() -> some PublicProto { // expected-n // MARK: - Nominal types public protocol PublicProto { - func req() -> Int + func req() -> Int // expected-note 2 {{protocol requires function 'req()' with type '() -> Int'; add a stub for conformance}} } protocol InternalProto { - // FIXME: Serialization causes typechecking of protocols regardless of access level -// func req() -> DoesNotExist + func goodReq() -> Int // expected-note {{protocol requires function 'goodReq()' with type '() -> Int'; add a stub for conformance}} + func badReq() -> DoesNotExist // expected-error {{cannot find type 'DoesNotExist' in scope}} } public struct PublicStruct { @@ -114,6 +114,37 @@ public class PublicClass { class InternalClass: DoesNotExist { // expected-error {{cannot find type 'DoesNotExist' in scope}} init(x: DoesNotExist) {} // expected-error {{cannot find type 'DoesNotExist' in scope}} } + +// MARK: - Conformances + +public struct PublicStructConformingToPublicProto: PublicProto { + public init() {} + public func req() -> Int { + return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}} + } +} + +public class PublicClassConformingToPublicProto: PublicProto { + public init() {} + public func req() -> Int { + return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}} + } +} + +extension String: PublicProto { + public func req() -> Int { + return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}} + } +} + +struct InternalStructConformingToPublicProto: PublicProto { // expected-error {{type 'InternalStructConformingToPublicProto' does not conform to protocol 'PublicProto'}} +} + +extension InternalStruct: PublicProto { // expected-error {{type 'InternalStruct' does not conform to protocol 'PublicProto'}} +} + +struct InternalStructConformingToInternalProto: InternalProto { // expected-error {{type 'InternalStructConformingToInternalProto' does not conform to protocol 'InternalProto'}} +} + // FIXME: Test enums -// FIXME: Test conformances // FIXME: Test global vars diff --git a/test/Inputs/lazy_typecheck_client.swift b/test/Inputs/lazy_typecheck_client.swift index 6336f9e72211f..ce31cabf2dfad 100644 --- a/test/Inputs/lazy_typecheck_client.swift +++ b/test/Inputs/lazy_typecheck_client.swift @@ -31,3 +31,11 @@ func testPublicClass() { _ = c.publicMethod() PublicClass.publicClassMethod() } + +func testConformances() { + let _: [any PublicProto] = [ + PublicStructConformingToPublicProto(), + PublicClassConformingToPublicProto(), + "string", + ] +} diff --git a/test/ModuleInterface/lazy-typecheck.swift b/test/ModuleInterface/lazy-typecheck.swift index c20f8585bc1a6..caf0ae2adb608 100644 --- a/test/ModuleInterface/lazy-typecheck.swift +++ b/test/ModuleInterface/lazy-typecheck.swift @@ -29,16 +29,26 @@ // CHECK: public protocol PublicProto { // CHECK: func req() -> Swift.Int // CHECK: } - // CHECK: public struct PublicStruct { // CHECK: public init(x: Swift.Int) // CHECK: public func publicMethod() -> Swift.Int // CHECK: public static func publicStaticMethod() // CHECK: } - // CHECK: public class PublicClass { // CHECK: public init(x: Swift.Int) // CHECK: public func publicMethod() -> Swift.Int // CHECK: public class func publicClassMethod() // CHECK: deinit // CHECK: } +// CHECK: public struct PublicStructConformingToPublicProto : PublicProto { +// CHECK: public init() +// CHECK: public func req() -> Swift.Int +// CHECK: } +// CHECK: public class PublicClassConformingToPublicProto : PublicProto { +// CHECK: public init() +// CHECK: public func req() -> Swift.Int +// CHECK: deinit +// CHECK: } +// CHECK: extension Swift.String : PublicProto { +// CHECK: public func req() -> Swift.Int +// CHECK: } diff --git a/test/TBD/lazy-typecheck.swift b/test/TBD/lazy-typecheck.swift index 6e5e126179ddf..3e731af4a3ea3 100644 --- a/test/TBD/lazy-typecheck.swift +++ b/test/TBD/lazy-typecheck.swift @@ -17,7 +17,30 @@ compatibility-version: 0 swift-abi-version: 7 exports: - targets: [ arm64-macos ] - symbols: [ '_$s14lazy_typecheck10publicFuncSiyF', '_$s14lazy_typecheck11PublicClassC06publicD6MethodyyFZTj', + symbols: [ '_$s14lazy_typecheck023PublicClassConformingToC5ProtoC3reqSiyFTj', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoC3reqSiyFTq', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCAA0cG0AAMc', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCAA0cG0AAWP', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCACycfC', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCACycfCTj', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCACycfCTq', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCACycfc', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCMa', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCMm', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCMn', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCMo', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCMu', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCN', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCfD', + '_$s14lazy_typecheck023PublicClassConformingToC5ProtoCfd', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoV3reqSiyF', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVAA0cG0AAMc', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVAA0cG0AAWP', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVACycfC', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVMa', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVMn', + '_$s14lazy_typecheck024PublicStructConformingToC5ProtoVN', + '_$s14lazy_typecheck10publicFuncSiyF', '_$s14lazy_typecheck11PublicClassC06publicD6MethodyyFZTj', '_$s14lazy_typecheck11PublicClassC06publicD6MethodyyFZTq', '_$s14lazy_typecheck11PublicClassC12publicMethodSiyFTj', '_$s14lazy_typecheck11PublicClassC12publicMethodSiyFTq', '_$s14lazy_typecheck11PublicClassC1xACSi_tcfC', '_$s14lazy_typecheck11PublicClassC1xACSi_tcfCTj', @@ -35,5 +58,7 @@ exports: '_$s14lazy_typecheck13inlinableFuncSiyF', '_$s14lazy_typecheck24publicFuncWithDefaultArgyS2iF', '_$s14lazy_typecheck30publicFuncWithOpaqueReturnTypeQryF', '_$s14lazy_typecheck30publicFuncWithOpaqueReturnTypeQryFQOMQ', - '_$s14lazy_typecheck32constrainedGenericPublicFunctionyyxAA0E5ProtoRzlF' ] + '_$s14lazy_typecheck32constrainedGenericPublicFunctionyyxAA0E5ProtoRzlF', + '_$sSS14lazy_typecheck11PublicProtoAAMc', '_$sSS14lazy_typecheck11PublicProtoAAWP', + '_$sSS14lazy_typecheckE3reqSiyF' ] ... From fde20160d3ef9f6bec182a11770ac554e55658f3 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 30 Aug 2023 11:39:55 -0700 Subject: [PATCH 2/2] TBDGen: Avoid crash when value witness is missing in conformance. --- lib/SIL/IR/SILSymbolVisitor.cpp | 3 +++ test/TBD/lazy-typecheck-bad-conformance.swift | 10 ++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/TBD/lazy-typecheck-bad-conformance.swift diff --git a/lib/SIL/IR/SILSymbolVisitor.cpp b/lib/SIL/IR/SILSymbolVisitor.cpp index abb8dfcd2b77b..b913316b9cd63 100644 --- a/lib/SIL/IR/SILSymbolVisitor.cpp +++ b/lib/SIL/IR/SILSymbolVisitor.cpp @@ -287,6 +287,9 @@ class SILSymbolVisitorImpl : public ASTVisitor { rootConformance->forEachValueWitness([&](ValueDecl *valueReq, Witness witness) { auto witnessDecl = witness.getDecl(); + if (!witnessDecl) + return; + if (isa(valueReq)) { addSymbolIfNecessary(valueReq, witnessDecl); } else if (auto *storage = dyn_cast(valueReq)) { diff --git a/test/TBD/lazy-typecheck-bad-conformance.swift b/test/TBD/lazy-typecheck-bad-conformance.swift new file mode 100644 index 0000000000000..f69e84e006ad7 --- /dev/null +++ b/test/TBD/lazy-typecheck-bad-conformance.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -experimental-lazy-typecheck -emit-tbd -emit-tbd-path %t/lazy.tbd %s -enable-library-evolution -parse-as-library + +public protocol P { + func req() +} + +// FIXME: This malformed conformance should probably be diagnosed. +public struct S: P { +}