mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
To pave the way for the new experimental feature which will operate on '@const' attribute and expand the scope of what's currently handled by '_const' without breaking compatibility, for now.
472 lines
16 KiB
C++
472 lines
16 KiB
C++
//===--- RequirementMatch.h - Result of conformance check -------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef SWIFT_AST_REQUIREMENTMATCH_H
|
|
#define SWIFT_AST_REQUIREMENTMATCH_H
|
|
|
|
#include "swift/AST/RequirementEnvironment.h"
|
|
#include "swift/AST/Type.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/Witness.h"
|
|
#include "swift/Basic/Debug.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
namespace swift {
|
|
|
|
/// The result of matching a particular declaration to a given
|
|
/// requirement.
|
|
enum class MatchKind : uint8_t {
|
|
/// The witness matched the requirement exactly.
|
|
ExactMatch,
|
|
|
|
/// The witness has fewer effects than the requirement, which is okay.
|
|
FewerEffects,
|
|
|
|
/// The witness is @Sendable and the requirement is not. Okay in certain
|
|
/// language modes.
|
|
RequiresNonSendable,
|
|
|
|
/// There is a difference in optionality.
|
|
OptionalityConflict,
|
|
|
|
/// The witness matched the requirement with some renaming.
|
|
RenamedMatch,
|
|
|
|
/// The witness is invalid or has an invalid type.
|
|
WitnessInvalid,
|
|
|
|
/// The witness is currently being type checked and this type checking in turn
|
|
/// triggered conformance checking, so the witness cannot be considered as a
|
|
/// candidate.
|
|
Circularity,
|
|
|
|
/// The kind of the witness and requirement differ, e.g., one
|
|
/// is a function and the other is a variable.
|
|
KindConflict,
|
|
|
|
/// The types conflict.
|
|
TypeConflict,
|
|
|
|
/// The witness would match if an additional requirement were met.
|
|
MissingRequirement,
|
|
|
|
/// The witness and requirement disagree on 'async'.
|
|
AsyncConflict,
|
|
|
|
/// The witness throws, but the requirement does not.
|
|
ThrowsConflict,
|
|
|
|
/// The witness did not match due to static/non-static differences.
|
|
StaticNonStaticConflict,
|
|
|
|
/// The witness is not settable, but the requirement is.
|
|
SettableConflict,
|
|
|
|
/// The witness did not match due to prefix/non-prefix differences.
|
|
PrefixNonPrefixConflict,
|
|
|
|
/// The witness did not match due to postfix/non-postfix differences.
|
|
PostfixNonPostfixConflict,
|
|
|
|
/// The witness did not match because of mutating conflicts.
|
|
MutatingConflict,
|
|
|
|
/// The witness did not match because of nonmutating conflicts.
|
|
NonMutatingConflict,
|
|
|
|
/// The witness did not match because of __consuming conflicts.
|
|
ConsumingConflict,
|
|
|
|
/// The witness throws unconditionally, but the requirement rethrows.
|
|
RethrowsConflict,
|
|
|
|
/// The witness rethrows via conformance, but the requirement rethrows
|
|
/// via closure and is not in a '@rethrows' protocol.
|
|
RethrowsByConformanceConflict,
|
|
|
|
/// The witness is explicitly @nonobjc but the requirement is @objc.
|
|
NonObjC,
|
|
|
|
/// The witness is missing a `@differentiable` attribute from the requirement.
|
|
MissingDifferentiableAttr,
|
|
|
|
/// The witness did not match because it is an enum case with
|
|
/// associated values.
|
|
EnumCaseWithAssociatedValues,
|
|
|
|
/// The witness did not match due to _const/non-_const differences.
|
|
CompileTimeLiteralConflict,
|
|
};
|
|
|
|
// Describes the kind of optional adjustment performed when
|
|
/// comparing two types.
|
|
enum class OptionalAdjustmentKind {
|
|
// No adjustment required.
|
|
None,
|
|
|
|
/// The witness can produce a 'nil' that won't be handled by
|
|
/// callers of the requirement. This is a type-safety problem.
|
|
ProducesUnhandledNil,
|
|
|
|
/// Callers of the requirement can provide 'nil', but the witness
|
|
/// does not handle it. This is a type-safety problem.
|
|
ConsumesUnhandledNil,
|
|
|
|
/// The witness handles 'nil', but won't ever be given a 'nil'.
|
|
/// This is not a type-safety problem.
|
|
WillNeverConsumeNil,
|
|
|
|
/// Callers of the requirement can expect to receive 'nil', but
|
|
/// the witness will never produce one. This is not a type-safety
|
|
/// problem.
|
|
WillNeverProduceNil,
|
|
|
|
/// The witness has an IUO that can be removed, because the
|
|
/// protocol doesn't need it. This is not a type-safety problem.
|
|
RemoveIUO,
|
|
|
|
/// The witness has an IUO that should be translated into a true
|
|
/// optional. This is not a type-safety problem.
|
|
IUOToOptional,
|
|
};
|
|
|
|
/// Describes an optional adjustment made to a witness.
|
|
class OptionalAdjustment {
|
|
/// The kind of adjustment.
|
|
unsigned Kind : 16;
|
|
|
|
/// Whether this is a parameter adjustment (with an index) vs. a
|
|
/// result or value type adjustment (no index needed).
|
|
unsigned IsParameterAdjustment : 1;
|
|
|
|
/// The adjustment index, for parameter adjustments.
|
|
unsigned ParameterAdjustmentIndex : 15;
|
|
|
|
public:
|
|
/// Create a non-parameter optional adjustment.
|
|
explicit OptionalAdjustment(OptionalAdjustmentKind kind)
|
|
: Kind(static_cast<unsigned>(kind)), IsParameterAdjustment(false),
|
|
ParameterAdjustmentIndex(0) { }
|
|
|
|
/// Create an optional adjustment to a parameter.
|
|
OptionalAdjustment(OptionalAdjustmentKind kind,
|
|
unsigned parameterIndex)
|
|
: Kind(static_cast<unsigned>(kind)), IsParameterAdjustment(true),
|
|
ParameterAdjustmentIndex(parameterIndex) { }
|
|
|
|
/// Determine the kind of optional adjustment.
|
|
OptionalAdjustmentKind getKind() const {
|
|
return static_cast<OptionalAdjustmentKind>(Kind);
|
|
}
|
|
|
|
/// Determine whether this is a parameter adjustment.
|
|
bool isParameterAdjustment() const {
|
|
return IsParameterAdjustment;
|
|
}
|
|
|
|
/// Return the index of a parameter adjustment.
|
|
unsigned getParameterIndex() const {
|
|
assert(isParameterAdjustment() && "Not a parameter adjustment");
|
|
return ParameterAdjustmentIndex;
|
|
}
|
|
|
|
/// Determines whether the optional adjustment is an error.
|
|
bool isError() const {
|
|
switch (getKind()) {
|
|
case OptionalAdjustmentKind::None:
|
|
return false;
|
|
|
|
case OptionalAdjustmentKind::ProducesUnhandledNil:
|
|
case OptionalAdjustmentKind::ConsumesUnhandledNil:
|
|
return true;
|
|
|
|
case OptionalAdjustmentKind::WillNeverConsumeNil:
|
|
case OptionalAdjustmentKind::WillNeverProduceNil:
|
|
case OptionalAdjustmentKind::RemoveIUO:
|
|
case OptionalAdjustmentKind::IUOToOptional:
|
|
// Warnings at most.
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled OptionalAdjustmentKind in switch.");
|
|
}
|
|
|
|
/// Retrieve the source location at which the optional is
|
|
/// specified or would be inserted.
|
|
SourceLoc getOptionalityLoc(ValueDecl *witness) const;
|
|
|
|
/// Retrieve the optionality location for the given type
|
|
/// representation.
|
|
SourceLoc getOptionalityLoc(TypeRepr *tyR) const;
|
|
};
|
|
|
|
/// Once a witness has been found, there are several reasons it may
|
|
/// not be usable.
|
|
enum class CheckKind : unsigned {
|
|
/// The witness is OK.
|
|
Success,
|
|
|
|
/// The witness is less accessible than the requirement.
|
|
Access,
|
|
|
|
/// The witness is storage whose setter is less accessible than the
|
|
/// requirement.
|
|
AccessOfSetter,
|
|
|
|
/// The witness needs to be @usableFromInline.
|
|
UsableFromInline,
|
|
|
|
/// The witness is less available than the requirement.
|
|
Availability,
|
|
|
|
/// The requirement was marked explicitly unavailable.
|
|
Unavailable,
|
|
|
|
/// The witness requires optional adjustments.
|
|
OptionalityConflict,
|
|
|
|
/// The witness is a constructor which is more failable than the
|
|
/// requirement.
|
|
ConstructorFailability,
|
|
|
|
/// The witness itself is inaccessible.
|
|
WitnessUnavailable,
|
|
|
|
/// The witness is a deprecated default implementation provided by the
|
|
/// protocol.
|
|
DefaultWitnessDeprecated,
|
|
};
|
|
/// Describes the suitability of the chosen witness for
|
|
/// the requirement.
|
|
struct RequirementCheck {
|
|
CheckKind Kind;
|
|
|
|
/// The required access scope, if the check failed due to the
|
|
/// witness being less accessible than the requirement.
|
|
AccessScope RequiredAccessScope;
|
|
|
|
/// The required availability, if the check failed due to the
|
|
/// witness being less available than the requirement.
|
|
AvailabilityRange RequiredAvailability;
|
|
|
|
RequirementCheck(CheckKind kind)
|
|
: Kind(kind), RequiredAccessScope(AccessScope::getPublic()),
|
|
RequiredAvailability(AvailabilityRange::alwaysAvailable()) {}
|
|
|
|
RequirementCheck(CheckKind kind, AccessScope requiredAccessScope)
|
|
: Kind(kind), RequiredAccessScope(requiredAccessScope),
|
|
RequiredAvailability(AvailabilityRange::alwaysAvailable()) {}
|
|
|
|
RequirementCheck(CheckKind kind, AvailabilityRange requiredAvailability)
|
|
: Kind(kind), RequiredAccessScope(AccessScope::getPublic()),
|
|
RequiredAvailability(requiredAvailability) {}
|
|
};
|
|
|
|
|
|
/// Describes a match between a requirement and a witness.
|
|
struct RequirementMatch {
|
|
RequirementMatch(ValueDecl *witness, MatchKind kind,
|
|
std::optional<RequirementEnvironment> env = std::nullopt)
|
|
: Witness(witness), Kind(kind), WitnessType(), ReqEnv(std::move(env)) {
|
|
assert(!hasWitnessType() && "Should have witness type");
|
|
}
|
|
|
|
RequirementMatch(ValueDecl *witness, MatchKind kind,
|
|
const DeclAttribute *attr)
|
|
: Witness(witness), Kind(kind), WitnessType(), UnmetAttribute(attr),
|
|
ReqEnv(std::nullopt) {
|
|
assert(!hasWitnessType() && "Should have witness type");
|
|
assert(hasUnmetAttribute() && "Should have unmet attribute");
|
|
}
|
|
|
|
RequirementMatch(ValueDecl *witness, MatchKind kind, Type witnessType,
|
|
std::optional<RequirementEnvironment> env = std::nullopt,
|
|
ArrayRef<OptionalAdjustment> optionalAdjustments = {},
|
|
GenericSignature derivativeGenSig = GenericSignature())
|
|
: Witness(witness), Kind(kind), WitnessType(witnessType),
|
|
ReqEnv(std::move(env)), OptionalAdjustments(optionalAdjustments.begin(),
|
|
optionalAdjustments.end()),
|
|
DerivativeGenSig(derivativeGenSig) {
|
|
assert(hasWitnessType() == !witnessType.isNull() &&
|
|
"Should (or should not) have witness type");
|
|
}
|
|
|
|
RequirementMatch(ValueDecl *witness, MatchKind kind, Requirement requirement,
|
|
std::optional<RequirementEnvironment> env = std::nullopt,
|
|
ArrayRef<OptionalAdjustment> optionalAdjustments = {},
|
|
GenericSignature derivativeGenSig = GenericSignature())
|
|
: Witness(witness), Kind(kind), WitnessType(requirement.getFirstType()),
|
|
MissingRequirement(requirement), ReqEnv(std::move(env)),
|
|
OptionalAdjustments(optionalAdjustments.begin(),
|
|
optionalAdjustments.end()),
|
|
DerivativeGenSig(derivativeGenSig) {
|
|
assert(hasWitnessType() && hasRequirement() &&
|
|
"Should have witness type and requirement");
|
|
}
|
|
|
|
/// The witness that matches the (implied) requirement.
|
|
ValueDecl *Witness;
|
|
|
|
/// The kind of match.
|
|
MatchKind Kind;
|
|
|
|
/// The type of the witness when it is referenced.
|
|
Type WitnessType;
|
|
|
|
/// Requirement not met.
|
|
std::optional<Requirement> MissingRequirement;
|
|
|
|
/// Unmet attribute from the requirement.
|
|
const DeclAttribute *UnmetAttribute = nullptr;
|
|
|
|
/// The requirement environment to use for the witness thunk.
|
|
std::optional<RequirementEnvironment> ReqEnv;
|
|
|
|
/// The set of optional adjustments performed on the witness.
|
|
SmallVector<OptionalAdjustment, 2> OptionalAdjustments;
|
|
|
|
/// Substitutions mapping the type of the witness to the requirement
|
|
/// environment.
|
|
SubstitutionMap WitnessSubstitutions;
|
|
|
|
/// The matched derivative generic signature.
|
|
GenericSignature DerivativeGenSig;
|
|
|
|
/// Determine whether this match is well-formed, meaning that it is any
|
|
/// difference determined by requirement matching is acceptable.
|
|
bool isWellFormed() const {
|
|
switch(Kind) {
|
|
case MatchKind::ExactMatch:
|
|
case MatchKind::FewerEffects:
|
|
case MatchKind::RequiresNonSendable:
|
|
return true;
|
|
|
|
case MatchKind::OptionalityConflict:
|
|
case MatchKind::RenamedMatch:
|
|
case MatchKind::WitnessInvalid:
|
|
case MatchKind::Circularity:
|
|
case MatchKind::KindConflict:
|
|
case MatchKind::TypeConflict:
|
|
case MatchKind::MissingRequirement:
|
|
case MatchKind::StaticNonStaticConflict:
|
|
case MatchKind::CompileTimeLiteralConflict:
|
|
case MatchKind::SettableConflict:
|
|
case MatchKind::PrefixNonPrefixConflict:
|
|
case MatchKind::PostfixNonPostfixConflict:
|
|
case MatchKind::MutatingConflict:
|
|
case MatchKind::NonMutatingConflict:
|
|
case MatchKind::ConsumingConflict:
|
|
case MatchKind::RethrowsConflict:
|
|
case MatchKind::RethrowsByConformanceConflict:
|
|
case MatchKind::AsyncConflict:
|
|
case MatchKind::ThrowsConflict:
|
|
case MatchKind::NonObjC:
|
|
case MatchKind::MissingDifferentiableAttr:
|
|
case MatchKind::EnumCaseWithAssociatedValues:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled MatchKind in switch.");
|
|
}
|
|
|
|
/// Determine whether this match is viable, meaning that we could generate
|
|
/// a witness for it, even though there might be semantic errors.
|
|
bool isViable() const {
|
|
switch(Kind) {
|
|
case MatchKind::ExactMatch:
|
|
case MatchKind::FewerEffects:
|
|
case MatchKind::RequiresNonSendable:
|
|
case MatchKind::OptionalityConflict:
|
|
case MatchKind::RenamedMatch:
|
|
return true;
|
|
|
|
case MatchKind::WitnessInvalid:
|
|
case MatchKind::Circularity:
|
|
case MatchKind::KindConflict:
|
|
case MatchKind::TypeConflict:
|
|
case MatchKind::MissingRequirement:
|
|
case MatchKind::StaticNonStaticConflict:
|
|
case MatchKind::CompileTimeLiteralConflict:
|
|
case MatchKind::SettableConflict:
|
|
case MatchKind::PrefixNonPrefixConflict:
|
|
case MatchKind::PostfixNonPostfixConflict:
|
|
case MatchKind::MutatingConflict:
|
|
case MatchKind::NonMutatingConflict:
|
|
case MatchKind::ConsumingConflict:
|
|
case MatchKind::RethrowsConflict:
|
|
case MatchKind::RethrowsByConformanceConflict:
|
|
case MatchKind::AsyncConflict:
|
|
case MatchKind::ThrowsConflict:
|
|
case MatchKind::NonObjC:
|
|
case MatchKind::MissingDifferentiableAttr:
|
|
case MatchKind::EnumCaseWithAssociatedValues:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled MatchKind in switch.");
|
|
}
|
|
|
|
/// Determine whether this requirement match has a witness type.
|
|
bool hasWitnessType() const {
|
|
switch(Kind) {
|
|
case MatchKind::ExactMatch:
|
|
case MatchKind::FewerEffects:
|
|
case MatchKind::RequiresNonSendable:
|
|
case MatchKind::RenamedMatch:
|
|
case MatchKind::TypeConflict:
|
|
case MatchKind::MissingRequirement:
|
|
case MatchKind::OptionalityConflict:
|
|
return true;
|
|
|
|
case MatchKind::WitnessInvalid:
|
|
case MatchKind::Circularity:
|
|
case MatchKind::KindConflict:
|
|
case MatchKind::StaticNonStaticConflict:
|
|
case MatchKind::CompileTimeLiteralConflict:
|
|
case MatchKind::SettableConflict:
|
|
case MatchKind::PrefixNonPrefixConflict:
|
|
case MatchKind::PostfixNonPostfixConflict:
|
|
case MatchKind::MutatingConflict:
|
|
case MatchKind::NonMutatingConflict:
|
|
case MatchKind::ConsumingConflict:
|
|
case MatchKind::RethrowsConflict:
|
|
case MatchKind::RethrowsByConformanceConflict:
|
|
case MatchKind::AsyncConflict:
|
|
case MatchKind::ThrowsConflict:
|
|
case MatchKind::NonObjC:
|
|
case MatchKind::MissingDifferentiableAttr:
|
|
case MatchKind::EnumCaseWithAssociatedValues:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled MatchKind in switch.");
|
|
}
|
|
|
|
/// Determine whether this requirement match has a requirement.
|
|
bool hasRequirement() { return Kind == MatchKind::MissingRequirement; }
|
|
|
|
/// Determine whether this requirement match has an unmet attribute.
|
|
bool hasUnmetAttribute() {
|
|
return Kind == MatchKind::MissingDifferentiableAttr;
|
|
}
|
|
|
|
swift::Witness getWitness(ASTContext &ctx) const {
|
|
return swift::Witness(this->Witness, WitnessSubstitutions,
|
|
ReqEnv->getWitnessThunkSignature(),
|
|
ReqEnv->getRequirementToWitnessThunkSubs(),
|
|
DerivativeGenSig, std::nullopt);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // SWIFT_AST_REQUIREMENTMATCH_H
|