Skip to content

Revert "Merge pull request #33767 ..." #39067

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 1 commit into from
Aug 26, 2021
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
30 changes: 29 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
IsComputingSemanticMembers : 1
);

SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+8+16,
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+1+1+8+16,
/// Whether the \c RequiresClass bit is valid.
RequiresClassValid : 1,

Expand All @@ -527,6 +527,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// Whether the existential of this protocol conforms to itself.
ExistentialConformsToSelf : 1,

/// Whether the \c ExistentialTypeSupported bit is valid.
ExistentialTypeSupportedValid : 1,

/// Whether the existential of this protocol can be represented.
ExistentialTypeSupported : 1,

/// True if the protocol has requirements that cannot be satisfied (e.g.
/// because they could not be imported from Objective-C).
HasMissingRequirements : 1,
Expand Down Expand Up @@ -4145,6 +4151,21 @@ class ProtocolDecl final : public NominalTypeDecl {
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
}

/// Returns the cached result of \c existentialTypeSupported or \c None if it
/// hasn't yet been computed.
Optional<bool> getCachedExistentialTypeSupported() {
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
return Bits.ProtocolDecl.ExistentialTypeSupported;

return None;
}

/// Caches the result of \c existentialTypeSupported
void setCachedExistentialTypeSupported(bool supported) {
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
}

bool hasLazyRequirementSignature() const {
return Bits.ProtocolDecl.HasLazyRequirementSignature;
}
Expand All @@ -4154,6 +4175,7 @@ class ProtocolDecl final : public NominalTypeDecl {
friend class RequirementSignatureRequest;
friend class ProtocolRequiresClassRequest;
friend class ExistentialConformsToSelfRequest;
friend class ExistentialTypeSupportedRequest;
friend class InheritedProtocolsRequest;

public:
Expand Down Expand Up @@ -4242,6 +4264,12 @@ class ProtocolDecl final : public NominalTypeDecl {
/// contain 'Self' in 'parameter' or 'other' position.
bool isAvailableInExistential(const ValueDecl *decl) const;

/// Determine whether we are allowed to refer to an existential type
/// conforming to this protocol. This is only permitted if the types of
/// all the members do not contain any associated types, and do not
/// contain 'Self' in 'parameter' or 'other' position.
bool existentialTypeSupported() const;

/// Returns a list of protocol requirements that must be assessed to
/// determine a concrete's conformance effect polymorphism kind.
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,9 @@ NOTE(object_literal_resolve_import,none,

ERROR(use_local_before_declaration,none,
"use of local variable %0 before its declaration", (DeclNameRef))
ERROR(unsupported_existential_type,none,
"protocol %0 can only be used as a generic constraint because it has "
"Self or associated type requirements", (Identifier))

ERROR(decl_does_not_exist_in_module,none,
"%select{%error|type|struct|class|enum|protocol|variable|function}0 "
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/EducationalNotes.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

// EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...)

EDUCATIONAL_NOTES(could_not_use_member_on_existential,
"existential-member-access-limitations.md")
EDUCATIONAL_NOTES(unsupported_existential_type,
"associated-type-requirements.md")

EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral, "temporary-pointers.md")
EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral_warning,
Expand Down
26 changes: 26 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,32 @@ class ExistentialConformsToSelfRequest :
void cacheResult(bool value) const;
};

/// Determine whether we are allowed to refer to an existential type conforming
/// to this protocol.
class ExistentialTypeSupportedRequest :
public SimpleRequest<ExistentialTypeSupportedRequest,
bool(ProtocolDecl *),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
bool evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;

public:
// Cycle handling.
void diagnoseCycle(DiagnosticEngine &diags) const;
void noteCycleStep(DiagnosticEngine &diags) const;

// Separate caching.
bool isCached() const { return true; }
Optional<bool> getCachedResult() const;
void cacheResult(bool value) const;
};

class PolymorphicEffectRequirementsRequest :
public SimpleRequest<PolymorphicEffectRequirementsRequest,
PolymorphicEffectRequirementList(EffectKind, ProtocolDecl *),
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
Type(EnumDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest,
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest,
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached,
NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *),
Expand Down
52 changes: 20 additions & 32 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5008,31 +5008,24 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
return findProtocolSelfReferences(proto, selfType->getSelfType(), position);
}

if (auto *const nominal = type->getAs<NominalOrBoundGenericNominalType>()) {
// Most bound generic types are invariant.
if (auto *const bgt = type->getAs<BoundGenericType>()) {
auto info = SelfReferenceInfo();

// Don't forget to look in the parent.
if (const auto parent = nominal->getParent()) {
info |= findProtocolSelfReferences(proto, parent, position);
}

// Most bound generic types are invariant.
if (auto *const bgt = type->getAs<BoundGenericType>()) {
if (bgt->isArray()) {
// Swift.Array preserves variance in its Value type.
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
position);
} else if (bgt->isDictionary()) {
// Swift.Dictionary preserves variance in its Element type.
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
if (bgt->isArray()) {
// Swift.Array preserves variance in its Value type.
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
position);
} else if (bgt->isDictionary()) {
// Swift.Dictionary preserves variance in its Element type.
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
SelfReferencePosition::Invariant);
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
position);
} else {
for (auto paramType : bgt->getGenericArgs()) {
info |= findProtocolSelfReferences(proto, paramType,
SelfReferencePosition::Invariant);
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
position);
} else {
for (auto paramType : bgt->getGenericArgs()) {
info |= findProtocolSelfReferences(proto, paramType,
SelfReferencePosition::Invariant);
}
}
}

Expand All @@ -5044,16 +5037,6 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
if (type->is<OpaqueTypeArchetypeType>())
return SelfReferenceInfo::forSelfRef(SelfReferencePosition::Invariant);

// Protocol compositions preserve variance.
if (auto *comp = type->getAs<ProtocolCompositionType>()) {
// 'Self' may be referenced only in a superclass component.
if (const auto superclass = comp->getSuperclass()) {
return findProtocolSelfReferences(proto, superclass, position);
}

return SelfReferenceInfo();
}

// A direct reference to 'Self'.
if (proto->getSelfInterfaceType()->isEqual(type))
return SelfReferenceInfo::forSelfRef(position);
Expand Down Expand Up @@ -5153,6 +5136,11 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
return true;
}

bool ProtocolDecl::existentialTypeSupported() const {
return evaluateOrDefault(getASTContext().evaluator,
ExistentialTypeSupportedRequest{const_cast<ProtocolDecl *>(this)}, true);
}

StringRef ProtocolDecl::getObjCRuntimeName(
llvm::SmallVectorImpl<char> &buffer) const {
// If there is an 'objc' attribute with a name, use that name.
Expand Down
25 changes: 25 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,31 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
decl->setCachedExistentialConformsToSelf(value);
}

//----------------------------------------------------------------------------//
// existentialTypeSupported computation.
//----------------------------------------------------------------------------//

void ExistentialTypeSupportedRequest::diagnoseCycle(DiagnosticEngine &diags) const {
auto decl = std::get<0>(getStorage());
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
}

void ExistentialTypeSupportedRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto requirement = std::get<0>(getStorage());
diags.diagnose(requirement, diag::kind_declname_declared_here,
DescriptiveDeclKind::Protocol, requirement->getName());
}

Optional<bool> ExistentialTypeSupportedRequest::getCachedResult() const {
auto decl = std::get<0>(getStorage());
return decl->getCachedExistentialTypeSupported();
}

void ExistentialTypeSupportedRequest::cacheResult(bool value) const {
auto decl = std::get<0>(getStorage());
decl->setCachedExistentialTypeSupported(value);
}

//----------------------------------------------------------------------------//
// isFinal computation.
//----------------------------------------------------------------------------//
Expand Down
75 changes: 26 additions & 49 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7322,7 +7322,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
// Dig out the instance type and figure out what members of the instance type
// we are going to see.
auto baseTy = candidate.getBaseType();
const auto baseObjTy = baseTy->getRValueType();
auto baseObjTy = baseTy->getRValueType();

bool hasInstanceMembers = false;
bool hasInstanceMethods = false;
Expand Down Expand Up @@ -7369,6 +7369,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
hasInstanceMethods = true;
}

// If our base is an existential type, we can't make use of any
// member whose signature involves associated types.
if (instanceTy->isExistentialType()) {
if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) {
if (!proto->isAvailableInExistential(decl)) {
result.addUnviable(candidate,
MemberLookupResult::UR_UnavailableInExistential);
return;
}
}
}

// If the invocation's argument expression has a favored type,
// use that information to determine whether a specific overload for
// the candidate should be favored.
Expand All @@ -7388,20 +7400,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
}
}

const auto isUnsupportedExistentialMemberAccess = [&] {
// If our base is an existential type, we can't make use of any
// member whose signature involves associated types.
if (instanceTy->isExistentialType()) {
if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) {
if (!proto->isAvailableInExistential(decl)) {
return true;
}
}
}

return false;
};

// See if we have an instance method, instance member or static method,
// and check if it can be accessed on our base type.

Expand All @@ -7415,35 +7413,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
? candidate
: OverloadChoice(instanceTy, decl,
FunctionRefKind::SingleApply);

const bool invalidMethodRef = isa<FuncDecl>(decl) && !hasInstanceMethods;
const bool invalidMemberRef = !isa<FuncDecl>(decl) && !hasInstanceMembers;

if (invalidMethodRef || invalidMemberRef) {
// If this is definitely an invalid way to reference a method or member
// on the metatype, let's stop here.
result.addUnviable(choice,
MemberLookupResult::UR_InstanceMemberOnType);
return;
} else if (isUnsupportedExistentialMemberAccess()) {
// If the member reference itself is legal, but it turns out to be an
// unsupported existential member access, do not make further
// assumptions about the correctness of a potential call -- let
// the unsupported member access error prevail.
result.addUnviable(candidate,
MemberLookupResult::UR_UnavailableInExistential);
// If this is an instance member referenced from metatype
// let's add unviable result to the set because it could be
// either curried reference or an invalid call.
//
// New candidate shouldn't affect performance because such
// choice would only be attempted when solver is in diagnostic mode.
result.addUnviable(choice, MemberLookupResult::UR_InstanceMemberOnType);

bool invalidMethodRef = isa<FuncDecl>(decl) && !hasInstanceMethods;
bool invalidMemberRef = !isa<FuncDecl>(decl) && !hasInstanceMembers;
// If this is definitely an invalid way to reference a method or member
// on the metatype, let's stop here.
if (invalidMethodRef || invalidMemberRef)
return;
} else {
// Otherwise, still add an unviable result to the set, because it
// could be an invalid call that was supposed to be performed on an
// instance of the type.
//
// New candidate shouldn't affect performance because such
// choice would only be attempted when solver is in diagnostic mode.
result.addUnviable(choice,
MemberLookupResult::UR_InstanceMemberOnType);

}
}

// If the underlying type of a typealias is fully concrete, it is legal
Expand Down Expand Up @@ -7494,12 +7477,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
}
}

if (isUnsupportedExistentialMemberAccess()) {
result.addUnviable(candidate,
MemberLookupResult::UR_UnavailableInExistential);
return;
}

// If we have an rvalue base, make sure that the result isn't 'mutating'
// (only valid on lvalues).
if (!baseTy->is<AnyMetatypeType>() &&
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3329,6 +3329,8 @@ static void checkSwitch(ASTContext &ctx, const SwitchStmt *stmt) {
// We want to warn about "case .Foo, .Bar where 1 != 100:" since the where
// clause only applies to the second case, and this is surprising.
for (auto cs : stmt->getCases()) {
TypeChecker::checkUnsupportedProtocolType(ctx, cs);

// The case statement can have multiple case items, each can have a where.
// If we find a "where", and there is a preceding item without a where, and
// if they are on the same source line, then warn.
Expand Down Expand Up @@ -4745,6 +4747,8 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
auto &ctx = DC->getASTContext();

TypeChecker::checkUnsupportedProtocolType(ctx, const_cast<Stmt *>(S));

if (auto switchStmt = dyn_cast<SwitchStmt>(S))
checkSwitch(ctx, switchStmt);

Expand Down
28 changes: 28 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,34 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
return true;
}

bool
ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator,
ProtocolDecl *decl) const {
// ObjC protocols can always be existential.
if (decl->isObjC())
return true;

for (auto member : decl->getMembers()) {
// Existential types cannot be used if the protocol has an associated type.
if (isa<AssociatedTypeDecl>(member))
return false;

// For value members, look at their type signatures.
if (auto valueMember = dyn_cast<ValueDecl>(member)) {
if (!decl->isAvailableInExistential(valueMember))
return false;
}
}

// Check whether all of the inherited protocols support existential types.
for (auto proto : decl->getInheritedProtocols()) {
if (!proto->existentialTypeSupported())
return false;
}

return true;
}

bool
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (isa<ClassDecl>(decl))
Expand Down
Loading