Skip to content

Revert "[ParseableInterfaces] Skip value witnesses of resilient conformances" #20637

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 55 additions & 66 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,23 @@ bool WitnessChecker::findBestWitness(
}

if (numViable == 0) {
// Assume any missing value witnesses for a conformance in a parseable
// interface can be treated as opaque.
// FIXME: ...but we should do something better about types.
if (conformance && !conformance->isInvalid()) {
if (auto *SF = DC->getParentSourceFile()) {
if (SF->Kind == SourceFileKind::Interface) {
auto match = matchWitness(TC, ReqEnvironmentCache, Proto,
conformance, DC, requirement, requirement);
assert(match.isViable());
numViable = 1;
bestIdx = matches.size();
matches.push_back(std::move(match));
return true;
}
}
}

if (anyFromUnconstrainedExtension &&
conformance != nullptr &&
conformance->isInvalid()) {
Expand Down Expand Up @@ -2892,23 +2909,6 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
}
}

ResolveWitnessResult
ConformanceChecker::resolveWitnessAsOpaque(ValueDecl *requirement) {
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
auto match = matchWitness(TC, ReqEnvironmentCache, Proto,
Conformance, DC, requirement, requirement);
recordWitness(requirement, match);
return ResolveWitnessResult::Success;
}

static bool isConformanceFromParseableInterface(
const NormalProtocolConformance *conformance) {
auto *containingSF = conformance->getDeclContext()->getParentSourceFile();
if (!containingSF)
return false;
return containingSF->Kind == SourceFileKind::Interface;
}

ResolveWitnessResult
ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
Expand All @@ -2926,33 +2926,25 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
}
}

// Determine whether we can get a witness for this requirement some other way.
bool hasFallbacks = false;
if (!hasFallbacks)
hasFallbacks = requirement->getAttrs().hasAttribute<OptionalAttr>();
if (!hasFallbacks)
hasFallbacks = requirement->getAttrs().isUnavailable(TC.Context);
if (!hasFallbacks)
hasFallbacks = isConformanceFromParseableInterface(Conformance);

if (!hasFallbacks) {
// Can a witness for this requirement be derived for this nominal type?
if (auto derivable = DerivedConformance::getDerivableRequirement(
TC,
nominal,
requirement)) {
if (derivable == requirement) {
// If it's the same requirement, we can derive it here.
hasFallbacks = true;
} else {
// Otherwise, go satisfy the derivable requirement, which can introduce
// a member that could in turn satisfy *this* requirement.
auto derivableProto = cast<ProtocolDecl>(derivable->getDeclContext());
if (auto conformance =
TC.conformsToProtocol(Adoptee, derivableProto, DC, None)) {
if (conformance->isConcrete())
(void)conformance->getConcrete()->getWitnessDecl(derivable, &TC);
}
// Determine whether we can derive a witness for this requirement.
bool canDerive = false;

// Can a witness for this requirement be derived for this nominal type?
if (auto derivable = DerivedConformance::getDerivableRequirement(
TC,
nominal,
requirement)) {
if (derivable == requirement) {
// If it's the same requirement, we can derive it here.
canDerive = true;
} else {
// Otherwise, go satisfy the derivable requirement, which can introduce
// a member that could in turn satisfy *this* requirement.
auto derivableProto = cast<ProtocolDecl>(derivable->getDeclContext());
if (auto conformance =
TC.conformsToProtocol(Adoptee, derivableProto, DC, None)) {
if (conformance->isConcrete())
(void)conformance->getConcrete()->getWitnessDecl(derivable, &TC);
}
}
}
Expand All @@ -2963,7 +2955,11 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
unsigned bestIdx = 0;
bool doNotDiagnoseMatches = false;
bool ignoringNames = false;
if (findBestWitness(requirement, hasFallbacks ? nullptr : &ignoringNames,
bool considerRenames =
!canDerive && !requirement->getAttrs().hasAttribute<OptionalAttr>() &&
!requirement->getAttrs().isUnavailable(TC.Context);
if (findBestWitness(requirement,
considerRenames ? &ignoringNames : nullptr,
Conformance,
/* out parameters: */
matches, numViable, bestIdx, doNotDiagnoseMatches)) {
Expand Down Expand Up @@ -3181,7 +3177,19 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
// We have either no matches or an ambiguous match.

// If we can derive a definition for this requirement, just call it missing.
if (hasFallbacks) {
if (canDerive) {
return ResolveWitnessResult::Missing;
}

// If the requirement is optional, it's okay. We'll satisfy this via
// our handling of default definitions.
//
// FIXME: revisit this once we get default definitions in protocol bodies.
//
// Treat 'unavailable' implicitly as if it were 'optional'.
// The compiler will reject actual uses.
auto Attrs = requirement->getAttrs();
if (Attrs.hasAttribute<OptionalAttr>() || Attrs.isUnavailable(TC.Context)) {
return ResolveWitnessResult::Missing;
}

Expand Down Expand Up @@ -3344,29 +3352,10 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc,

ResolveWitnessResult
ConformanceChecker::resolveWitnessTryingAllStrategies(ValueDecl *requirement) {
using ResolveWitnessStrategy =
decltype(&ConformanceChecker::resolveWitnessViaLookup);
static const constexpr ResolveWitnessStrategy defaultStrategies[] = {
decltype(&ConformanceChecker::resolveWitnessViaLookup) strategies[] = {
&ConformanceChecker::resolveWitnessViaLookup,
&ConformanceChecker::resolveWitnessViaDerivation,
&ConformanceChecker::resolveWitnessViaDefault};
ArrayRef<ResolveWitnessStrategy> strategies = defaultStrategies;

// Don't try any sort of derivation when processing a parseable interface.
if (isConformanceFromParseableInterface(Conformance)) {
if (Conformance->isResilient()) {
// Resilient conformances don't allow any sort of devirtualization, so
// don't bother looking up witnesses at all.
static const constexpr ResolveWitnessStrategy resilientStrategies[] = {
&ConformanceChecker::resolveWitnessAsOpaque};
strategies = resilientStrategies;
} else {
static const constexpr ResolveWitnessStrategy interfaceStrategies[] = {
&ConformanceChecker::resolveWitnessViaLookup,
&ConformanceChecker::resolveWitnessAsOpaque};
strategies = interfaceStrategies;
}
}

for (auto strategy : strategies) {
ResolveWitnessResult result = (this->*strategy)(requirement);
Expand Down
3 changes: 0 additions & 3 deletions lib/Sema/TypeCheckProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,6 @@ class ConformanceChecker : public WitnessChecker {
void checkNonFinalClassWitness(ValueDecl *requirement,
ValueDecl *witness);

/// Resolve the (non-type) witness as requiring dynamic dispatch.
ResolveWitnessResult resolveWitnessAsOpaque(ValueDecl *requirement);

/// Resolve a (non-type) witness via name lookup.
ResolveWitnessResult resolveWitnessViaLookup(ValueDecl *requirement);

Expand Down
12 changes: 0 additions & 12 deletions test/ParseableInterface/Conformances.swiftinterface
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,6 @@ public struct OpaqueStructImpl: MyProto {}
// CHECK: function_ref @$s12Conformances7MyProtoPyS2icig
// CHECK: end sil function '$s16ConformancesUser10testOpaqueSiyF'

public struct ResilientStructImpl: MyProto {
@available(*, unavailable) // Ignore this, because the conformance is resilient.
public init()
}

// CHECK-LABEL: sil @$s16ConformancesUser13testResilientSiyF
// CHECK: witness_method $ResilientStructImpl, #MyProto.init!allocator.1
// CHECK: witness_method $ResilientStructImpl, #MyProto.method!1
// CHECK: witness_method $ResilientStructImpl, #MyProto.prop!setter.1
// CHECK: witness_method $ResilientStructImpl, #MyProto.subscript!getter.1
// CHECK: end sil function '$s16ConformancesUser13testResilientSiyF'


@objc public protocol OptionalReqs {
@objc optional func method()
Expand Down
4 changes: 0 additions & 4 deletions test/ParseableInterface/Inputs/ConformancesUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ public func testOpaque() -> Int {
return testGeneric(OpaqueStructImpl.self)
}

public func testResilient() -> Int {
return testGeneric(ResilientStructImpl.self)
}


func testOptionalGeneric<T: OptionalReqs>(_ obj: T) -> Bool {
return obj.method?() != nil
Expand Down