Skip to content

[5.6][SE-0335] Introduce existential any #40505

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 16 commits into from
Dec 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c75a1ec
[FrontendOptions] Add a frontend flag -enable-explicit-existential-types
hborla Nov 19, 2021
8531361
[AST] Introduce ExistentialTypeRepr, which is the type repr for an
hborla Nov 11, 2021
7bd3e83
[Type System] Introduce a dedicated type to represent existential types.
hborla Dec 7, 2021
f4dabf9
[TypeResolver] Resolve existential types to ExistentialType when
hborla Nov 21, 2021
bbbb131
Revert "NFC: Remove the now dead ProtocolDecl::existentialTypeSupport…
hborla Nov 11, 2021
922b206
Revert "[NFC, Typechecker] Remove UnsupportedProtocolVisitor and chec…
hborla Nov 11, 2021
b58c324
[Serialization] Bump the module format version for existential `any`
hborla Nov 13, 2021
6d00a24
[Sema] Rename existentialTypeSupported to existentialRequiresAny, and
hborla Nov 18, 2021
ac5dc85
[NFC][Sema] Rename checkUnsupportedProtocolType to checkExistentialTy…
hborla Nov 18, 2021
4fafee7
[Sema] Require existential types with Self or associated type
hborla Nov 19, 2021
b489450
[Parse] Parse 'any' as a contextual keyword.
hborla Nov 19, 2021
1e65a3d
[Test] Start to add tests for explicit existential types.
hborla Nov 21, 2021
f6b58cc
[ASTDemangler] Reconstruct protocol composition types with Existentia…
hborla Dec 9, 2021
c142194
[TypeResolver] Don't use ExistentialType for Any and AnyObject for
hborla Dec 10, 2021
323b28c
[ASTPrinter] Print 'any' for existential metatypes when explicit
hborla Dec 10, 2021
8e28817
[Test] Duplicate test/TypeDecoder/existentials.swift with explicit
hborla Dec 10, 2021
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
29 changes: 28 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,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 @@ -532,6 +532,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// Whether the existential of this protocol conforms to itself.
ExistentialConformsToSelf : 1,

/// Whether the \c ExistentialRequiresAny bit is valid.
ExistentialRequiresAnyValid : 1,

/// Whether the existential of this protocol must be spelled with \c any.
ExistentialRequiresAny : 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 @@ -4224,6 +4230,21 @@ class ProtocolDecl final : public NominalTypeDecl {
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
}

/// Returns the cached result of \c existentialRequiresAny or \c None if it
/// hasn't yet been computed.
Optional<bool> getCachedExistentialRequiresAny() {
if (Bits.ProtocolDecl.ExistentialRequiresAnyValid)
return Bits.ProtocolDecl.ExistentialRequiresAny;

return None;
}

/// Caches the result of \c existentialRequiresAny
void setCachedExistentialRequiresAny(bool requiresAny) {
Bits.ProtocolDecl.ExistentialRequiresAnyValid = true;
Bits.ProtocolDecl.ExistentialRequiresAny = requiresAny;
}

bool hasLazyRequirementSignature() const {
return Bits.ProtocolDecl.HasLazyRequirementSignature;
}
Expand All @@ -4237,6 +4258,7 @@ class ProtocolDecl final : public NominalTypeDecl {
friend class RequirementSignatureRequestRQM;
friend class ProtocolRequiresClassRequest;
friend class ExistentialConformsToSelfRequest;
friend class ExistentialRequiresAnyRequest;
friend class InheritedProtocolsRequest;

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

/// Determine whether an existential type must be explicitly prefixed
/// with \c any. \c any is required if any of the members contain
/// an associated type, or if \c Self appears in non-covariant position.
bool existentialRequiresAny() 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: 2 additions & 1 deletion include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,8 @@ ERROR(sil_function_subs_without_generics,none,

// Opaque types
ERROR(opaque_mid_composition,none,
"'some' should appear at the beginning of a composition", ())
"'%0' should appear at the beginning of a composition",
(StringRef))

//------------------------------------------------------------------------------
// MARK: Layout constraint diagnostics
Expand Down
13 changes: 13 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4618,6 +4618,19 @@ ERROR(unchecked_not_inheritance_clause,none,
ERROR(unchecked_not_existential,none,
"'unchecked' attribute cannot apply to non-protocol type %0", (Type))

WARNING(unnecessary_any,none,
"'any' is redundant on type %0", (Type))
ERROR(any_not_existential,none,
"'any' has no effect on %select{concrete type|type parameter}0 %1",
(bool, Type))
ERROR(existential_requires_any,none,
"protocol %0 as a type must be explicitly marked as 'any'",
(Identifier))
ERROR(explicit_existential_not_supported,none,
"explicit 'any' not supported; use frontend flag "
"-enable-explicit-existential-types to enable this feature",
())

ERROR(nonisolated_let,none,
"'nonisolated' is meaningless on 'let' declarations because "
"they are immutable",
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 an existential type conforming to this protocol
/// requires the \c any syntax.
class ExistentialRequiresAnyRequest :
public SimpleRequest<ExistentialRequiresAnyRequest,
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 @@ -92,6 +92,8 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
Type(EnumDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest,
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExistentialRequiresAnyRequest,
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached,
NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *),
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/TypeDifferenceVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ class CanTypeDifferenceVisitor : public CanTypePairVisitor<Impl, bool> {
type1->getMembers(), type2->getMembers());
}

bool visitExistentialType(CanExistentialType type1,
CanExistentialType type2) {
return asImpl().visit(type1.getConstraintType(),
type2.getConstraintType());
}

bool visitLValueType(CanLValueType type1, CanLValueType type2) {
return asImpl().visit(type1.getObjectType(), type2.getObjectType());
}
Expand Down
14 changes: 14 additions & 0 deletions include/swift/AST/TypeMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,20 @@ class TypeMatcher {
TRIVIAL_CASE(SILBoxType)
TRIVIAL_CASE(ProtocolCompositionType)

bool visitExistentialType(CanExistentialType firstExistential,
Type secondType,
Type sugaredFirstType) {
if (auto secondExistential = secondType->getAs<ExistentialType>()) {
return this->visit(firstExistential.getConstraintType(),
secondExistential->getConstraintType(),
sugaredFirstType->castTo<ExistentialType>()
->getConstraintType());
}

return mismatch(firstExistential.getPointer(), secondType,
sugaredFirstType);
}

bool visitLValueType(CanLValueType firstLValue, Type secondType,
Type sugaredFirstType) {
if (auto secondLValue = secondType->getAs<LValueType>()) {
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ ARTIFICIAL_TYPE(SILBlockStorage, Type)
ARTIFICIAL_TYPE(SILBox, Type)
ARTIFICIAL_TYPE(SILToken, Type)
TYPE(ProtocolComposition, Type)
TYPE(Existential, Type)
TYPE(LValue, Type)
TYPE(InOut, Type)
UNCHECKED_TYPE(TypeVariable, Type)
Expand Down
30 changes: 30 additions & 0 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,35 @@ class NamedOpaqueReturnTypeRepr : public TypeRepr {
friend class TypeRepr;
};

/// A TypeRepr for an existential type spelled with \c any
///
/// Can appear anywhere a normal existential type would. This is
/// purely a more explicit spelling for existential types.
class ExistentialTypeRepr: public TypeRepr {
TypeRepr *Constraint;
SourceLoc AnyLoc;

public:
ExistentialTypeRepr(SourceLoc anyLoc, TypeRepr *constraint)
: TypeRepr(TypeReprKind::Existential), Constraint(constraint),
AnyLoc(anyLoc) {}

TypeRepr *getConstraint() const { return Constraint; }
SourceLoc getAnyLoc() const { return AnyLoc; }

static bool classof(const TypeRepr *T) {
return T->getKind() == TypeReprKind::Existential;
}
static bool classof(const ExistentialTypeRepr *T) { return true; }

private:
SourceLoc getStartLocImpl() const { return AnyLoc; }
SourceLoc getEndLocImpl() const { return Constraint->getEndLoc(); }
SourceLoc getLocImpl() const { return AnyLoc; }
void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const;
friend class TypeRepr;
};

/// TypeRepr for a user-specified placeholder (essentially, a user-facing
/// representation of an anonymous type variable.
///
Expand Down Expand Up @@ -1285,6 +1314,7 @@ inline bool TypeRepr::isSimple() const {
case TypeReprKind::Composition:
case TypeReprKind::OpaqueReturn:
case TypeReprKind::NamedOpaqueReturn:
case TypeReprKind::Existential:
return false;
case TypeReprKind::SimpleIdent:
case TypeReprKind::GenericIdent:
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeReprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ TYPEREPR(Metatype, TypeRepr)
TYPEREPR(Protocol, TypeRepr)
TYPEREPR(OpaqueReturn, TypeRepr)
TYPEREPR(NamedOpaqueReturn, TypeRepr)
TYPEREPR(Existential, TypeRepr)
TYPEREPR(Placeholder, TypeRepr)
ABSTRACT_TYPEREPR(Specifier, TypeRepr)
TYPEREPR(InOut, SpecifierTypeRepr)
Expand Down
45 changes: 44 additions & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5199,6 +5199,42 @@ class ProtocolCompositionType final : public TypeBase,
BEGIN_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)
END_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)

/// An existential type, spelled with \c any .
///
/// In Swift 5 mode, a plain protocol name in type
/// context is an implicit existential type.
class ExistentialType final : public TypeBase {
Type ConstraintType;

ExistentialType(Type constraintType,
const ASTContext *canonicalContext,
RecursiveTypeProperties properties)
: TypeBase(TypeKind::Existential, canonicalContext, properties),
ConstraintType(constraintType) {}

public:
static ExistentialType *get(Type constraint);

Type getConstraintType() const { return ConstraintType; }

bool requiresClass() const {
if (auto protocol = ConstraintType->getAs<ProtocolType>())
return protocol->requiresClass();

if (auto composition = ConstraintType->getAs<ProtocolCompositionType>())
return composition->requiresClass();

return false;
}

static bool classof(const TypeBase *type) {
return type->getKind() == TypeKind::Existential;
}
};
BEGIN_CAN_TYPE_WRAPPER(ExistentialType, Type)
PROXY_CAN_TYPE_SIMPLE_GETTER(getConstraintType)
END_CAN_TYPE_WRAPPER(ExistentialType, Type)

/// LValueType - An l-value is a handle to a physical object. The
/// type of that object uniquely determines the type of an l-value
/// for it.
Expand Down Expand Up @@ -6154,7 +6190,9 @@ inline bool TypeBase::isAnyExistentialType() {
}

inline bool CanType::isExistentialTypeImpl(CanType type) {
return isa<ProtocolType>(type) || isa<ProtocolCompositionType>(type);
return (isa<ProtocolType>(type) ||
isa<ProtocolCompositionType>(type) ||
isa<ExistentialType>(type));
}

inline bool CanType::isAnyExistentialTypeImpl(CanType type) {
Expand All @@ -6167,6 +6205,8 @@ inline bool TypeBase::isClassExistentialType() {
return pt->requiresClass();
if (auto pct = dyn_cast<ProtocolCompositionType>(T))
return pct->requiresClass();
if (auto existential = dyn_cast<ExistentialType>(T))
return existential->requiresClass();
return false;
}

Expand Down Expand Up @@ -6427,6 +6467,9 @@ inline bool TypeBase::hasSimpleTypeRepr() const {
case TypeKind::ExistentialMetatype:
return !cast<const AnyMetatypeType>(this)->hasRepresentation();

case TypeKind::Existential:
return false;

case TypeKind::NestedArchetype:
return cast<NestedArchetypeType>(this)->getParent()->hasSimpleTypeRepr();

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ namespace swift {
/// `func f() -> <T> T`.
bool EnableExperimentalNamedOpaqueTypes = false;

/// Enable support for explicit existential types via the \c any
/// keyword.
bool EnableExplicitExistentialTypes = false;

/// Enable experimental flow-sensitive concurrent captures.
bool EnableExperimentalFlowSensitiveConcurrentCaptures = false;

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ def enable_experimental_structural_opaque_types :
Flag<["-"], "enable-experimental-structural-opaque-types">,
HelpText<"Enable experimental support for structural opaque result types">;

def enable_explicit_existential_types :
Flag<["-"], "enable-explicit-existential-types">,
HelpText<"Enable experimental support for explicit existential types">;

def enable_deserialization_recovery :
Flag<["-"], "enable-deserialization-recovery">,
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;
Expand Down
16 changes: 16 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ struct ASTContext::Implementation {
llvm::DenseMap<std::pair<StructDecl*, Type>, StructType*> StructTypes;
llvm::DenseMap<std::pair<ClassDecl*, Type>, ClassType*> ClassTypes;
llvm::DenseMap<std::pair<ProtocolDecl*, Type>, ProtocolType*> ProtocolTypes;
llvm::DenseMap<Type, ExistentialType *> ExistentialTypes;
llvm::FoldingSet<UnboundGenericType> UnboundGenericTypes;
llvm::FoldingSet<BoundGenericType> BoundGenericTypes;
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
Expand Down Expand Up @@ -4093,6 +4094,21 @@ ProtocolType::ProtocolType(ProtocolDecl *TheDecl, Type Parent,
RecursiveTypeProperties properties)
: NominalType(TypeKind::Protocol, &Ctx, TheDecl, Parent, properties) { }

ExistentialType *ExistentialType::get(Type constraint) {
auto properties = constraint->getRecursiveProperties();
auto arena = getArena(properties);

auto &C = constraint->getASTContext();
auto &entry = C.getImpl().getArena(arena).ExistentialTypes[constraint];
if (entry)
return entry;

const ASTContext *canonicalContext = constraint->isCanonical() ? &C : nullptr;
return entry = new (C, arena) ExistentialType(constraint,
canonicalContext,
properties);
}

LValueType *LValueType::get(Type objectTy) {
assert(!objectTy->is<LValueType>() && !objectTy->is<InOutType>() &&
"cannot have 'inout' or @lvalue wrapped inside an @lvalue");
Expand Down
9 changes: 8 additions & 1 deletion lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,12 @@ Type ASTBuilder::createProtocolCompositionType(
members.push_back(protocol->getDeclaredInterfaceType());
if (superclass && superclass->getClassOrBoundGenericClass())
members.push_back(superclass);
return ProtocolCompositionType::get(Ctx, members, isClassBound);
Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound);
if (Ctx.LangOpts.EnableExplicitExistentialTypes &&
!(composition->isAny() || composition->isAnyObject())) {
composition = ExistentialType::get(composition);
}
return composition;
}

static MetatypeRepresentation
Expand All @@ -607,6 +612,8 @@ getMetatypeRepresentation(ImplMetatypeRepresentation repr) {

Type ASTBuilder::createExistentialMetatypeType(Type instance,
Optional<Demangle::ImplMetatypeRepresentation> repr) {
if (auto existential = instance->getAs<ExistentialType>())
instance = existential->getConstraintType();
if (!instance->isAnyExistentialType())
return Type();
if (!repr)
Expand Down
Loading