From 7cdda6dc807e976291f5a3ac6f99682190c52d29 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 18 Nov 2021 22:31:55 -0800 Subject: [PATCH 01/16] [FrontendOptions] Add a frontend flag -enable-explicit-existential-types to enable the 'any' keyword. --- include/swift/Basic/LangOptions.h | 4 ++++ include/swift/Option/FrontendOptions.td | 4 ++++ lib/Frontend/CompilerInvocation.cpp | 3 +++ 3 files changed, 11 insertions(+) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 9e8f1ecfc6f..a1421fd5d97 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -310,6 +310,10 @@ namespace swift { /// `func f() -> 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; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 93a27768802..a380031b6c2 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -476,6 +476,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">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c286e252302..08fe38fb4c2 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -438,6 +438,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableExperimentalNamedOpaqueTypes |= Args.hasArg(OPT_enable_experimental_named_opaque_types); + Opts.EnableExplicitExistentialTypes |= + Args.hasArg(OPT_enable_explicit_existential_types); + Opts.EnableExperimentalDistributed |= Args.hasArg(OPT_enable_experimental_distributed); From 3ab0a78383c18635bfe6fbf17c3af55ac577227e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 10 Nov 2021 18:46:12 -0800 Subject: [PATCH 02/16] [AST] Introduce ExistentialTypeRepr, which is the type repr for an existential type spelled with `any`. --- include/swift/AST/DiagnosticsSema.def | 5 +++++ include/swift/AST/TypeRepr.h | 29 +++++++++++++++++++++++++++ include/swift/AST/TypeReprNodes.def | 1 + include/swift/AST/Types.h | 2 ++ lib/AST/ASTDumper.cpp | 6 ++++++ lib/AST/ASTWalker.cpp | 4 ++++ lib/AST/NameLookup.cpp | 1 + lib/AST/TypeRepr.cpp | 6 ++++++ lib/Sema/TypeCheckType.cpp | 10 +++++++++ 9 files changed, 64 insertions(+) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ac805477ad2..9e4faab8896 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4618,6 +4618,11 @@ ERROR(unchecked_not_inheritance_clause,none, ERROR(unchecked_not_existential,none, "'unchecked' attribute cannot apply to non-protocol type %0", (Type)) +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", diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 448df94a353..3aada9f4bee 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -1158,6 +1158,34 @@ private: 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; } + + 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. /// @@ -1285,6 +1313,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: diff --git a/include/swift/AST/TypeReprNodes.def b/include/swift/AST/TypeReprNodes.def index fee3fd287fc..3bb74cf05f7 100644 --- a/include/swift/AST/TypeReprNodes.def +++ b/include/swift/AST/TypeReprNodes.def @@ -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) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 9ddeb6462c2..40d35fe55db 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -6437,6 +6437,8 @@ inline bool TypeBase::hasSimpleTypeRepr() const { case TypeKind::ProtocolComposition: { // 'Any', 'AnyObject' and single protocol compositions are simple + // FIXME: single protocol compositions spelled with `any` are not + // simple. auto composition = cast(this); auto memberCount = composition->getMembers().size(); if (composition->hasExplicitAnyObject()) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 9b019396747..f91df702e50 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3095,6 +3095,12 @@ public: PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitExistentialTypeRepr(ExistentialTypeRepr *T) { + printCommon("type_existential"); + printRec(T->getConstraint()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitPlaceholderTypeRepr(PlaceholderTypeRepr *T) { printCommon("type_placeholder"); PrintWithColorRAII(OS, ParenthesisColor) << ')'; diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index d010c6dd2e8..59a4ef7888c 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1863,6 +1863,10 @@ bool Traversal::visitNamedOpaqueReturnTypeRepr(NamedOpaqueReturnTypeRepr *T) { return doIt(T->getBase()); } +bool Traversal::visitExistentialTypeRepr(ExistentialTypeRepr *T) { + return doIt(T->getConstraint()); +} + bool Traversal::visitPlaceholderTypeRepr(PlaceholderTypeRepr *T) { return false; } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index dfb758cc45c..8aa4844c2da 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2319,6 +2319,7 @@ directReferencesForTypeRepr(Evaluator &evaluator, case TypeReprKind::OpaqueReturn: case TypeReprKind::NamedOpaqueReturn: + case TypeReprKind::Existential: return { }; case TypeReprKind::Fixed: diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 99f853b9eb2..4062e1fa66f 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -479,6 +479,12 @@ void OpaqueReturnTypeRepr::printImpl(ASTPrinter &Printer, printTypeRepr(Constraint, Printer, Opts); } +void ExistentialTypeRepr::printImpl(ASTPrinter &Printer, + const PrintOptions &Opts) const { + Printer.printKeyword("any", Opts, /*Suffix=*/" "); + printTypeRepr(Constraint, Printer, Opts); +} + SourceLoc NamedOpaqueReturnTypeRepr::getStartLocImpl() const { return GenericParams->getLAngleLoc(); } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 944bb19cab0..e1e5631d11a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2040,6 +2040,16 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, : ErrorType::get(getASTContext()); } + case TypeReprKind::Existential: { + if (!getASTContext().LangOpts.EnableExplicitExistentialTypes && + !(options & TypeResolutionFlags::SilenceErrors)) { + diagnose(repr->getLoc(), diag::explicit_existential_not_supported); + } + + auto *existential = cast(repr); + return resolveType(existential->getConstraint(), options); + } + case TypeReprKind::NamedOpaqueReturn: return resolveType(cast(repr)->getBase(), options); From 445a8566529cd5f794c23399f921262bb1bd4098 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 7 Dec 2021 12:27:10 -0800 Subject: [PATCH 03/16] [Type System] Introduce a dedicated type to represent existential types. The new type, called ExistentialType, is not yet used in type resolution. Later, existential types written with `any` will resolve to this type, and bare protocol names will resolve to this type depending on context. --- include/swift/AST/DiagnosticsSema.def | 5 +++ include/swift/AST/TypeDifferenceVisitor.h | 6 +++ include/swift/AST/TypeMatcher.h | 14 +++++++ include/swift/AST/TypeNodes.def | 1 + include/swift/AST/TypeRepr.h | 1 + include/swift/AST/Types.h | 47 +++++++++++++++++++++-- lib/AST/ASTContext.cpp | 16 ++++++++ lib/AST/ASTDumper.cpp | 7 ++++ lib/AST/ASTMangler.cpp | 5 +++ lib/AST/ASTPrinter.cpp | 5 +++ lib/AST/NameLookup.cpp | 6 +++ lib/AST/Type.cpp | 32 +++++++++++++++ lib/AST/TypeWalker.cpp | 4 ++ lib/IRGen/Fulfillment.cpp | 4 ++ lib/IRGen/GenExistential.cpp | 8 ++++ lib/IRGen/GenType.cpp | 2 + lib/IRGen/GenType.h | 1 + lib/IRGen/IRGenDebugInfo.cpp | 1 + lib/IRGen/MetadataRequest.cpp | 6 +++ lib/PrintAsObjC/DeclAndTypePrinter.cpp | 6 +++ lib/Sema/CSApply.cpp | 2 + lib/Sema/CSSimplify.cpp | 15 +++++++- lib/Sema/LookupVisibleDecls.cpp | 7 ++++ lib/Sema/TypeCheckType.cpp | 28 ++++++++++++++ lib/Serialization/DeclTypeRecordNodes.def | 1 + lib/Serialization/Deserialization.cpp | 12 ++++++ lib/Serialization/ModuleFormat.h | 4 +- lib/Serialization/Serialization.cpp | 7 ++++ 28 files changed, 248 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9e4faab8896..772b6b82f76 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4618,6 +4618,11 @@ 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(explicit_existential_not_supported,none, "explicit 'any' not supported; use frontend flag " "-enable-explicit-existential-types to enable this feature", diff --git a/include/swift/AST/TypeDifferenceVisitor.h b/include/swift/AST/TypeDifferenceVisitor.h index 3cff6f0ecd8..98d476282d8 100644 --- a/include/swift/AST/TypeDifferenceVisitor.h +++ b/include/swift/AST/TypeDifferenceVisitor.h @@ -328,6 +328,12 @@ public: 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()); } diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index c0da030791a..4323e3ab720 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -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()) { + return this->visit(firstExistential.getConstraintType(), + secondExistential->getConstraintType(), + sugaredFirstType->castTo() + ->getConstraintType()); + } + + return mismatch(firstExistential.getPointer(), secondType, + sugaredFirstType); + } + bool visitLValueType(CanLValueType firstLValue, Type secondType, Type sugaredFirstType) { if (auto secondLValue = secondType->getAs()) { diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index 23c668e90b6..23b0696c2fc 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -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) diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 3aada9f4bee..94b27ae1a1c 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -1172,6 +1172,7 @@ public: AnyLoc(anyLoc) {} TypeRepr *getConstraint() const { return Constraint; } + SourceLoc getAnyLoc() const { return AnyLoc; } static bool classof(const TypeRepr *T) { return T->getKind() == TypeReprKind::Existential; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 40d35fe55db..33417bc9fe1 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5202,6 +5202,42 @@ private: 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()) + return protocol->requiresClass(); + + if (auto composition = ConstraintType->getAs()) + 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. @@ -6157,7 +6193,9 @@ inline bool TypeBase::isAnyExistentialType() { } inline bool CanType::isExistentialTypeImpl(CanType type) { - return isa(type) || isa(type); + return (isa(type) || + isa(type) || + isa(type)); } inline bool CanType::isAnyExistentialTypeImpl(CanType type) { @@ -6170,6 +6208,8 @@ inline bool TypeBase::isClassExistentialType() { return pt->requiresClass(); if (auto pct = dyn_cast(T)) return pct->requiresClass(); + if (auto existential = dyn_cast(T)) + return existential->requiresClass(); return false; } @@ -6428,6 +6468,9 @@ inline bool TypeBase::hasSimpleTypeRepr() const { case TypeKind::ExistentialMetatype: return !cast(this)->hasRepresentation(); + case TypeKind::Existential: + return false; + case TypeKind::NestedArchetype: return cast(this)->getParent()->hasSimpleTypeRepr(); @@ -6437,8 +6480,6 @@ inline bool TypeBase::hasSimpleTypeRepr() const { case TypeKind::ProtocolComposition: { // 'Any', 'AnyObject' and single protocol compositions are simple - // FIXME: single protocol compositions spelled with `any` are not - // simple. auto composition = cast(this); auto memberCount = composition->getMembers().size(); if (composition->hasExplicitAnyObject()) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index c08f4317c30..5e051972566 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -408,6 +408,7 @@ struct ASTContext::Implementation { llvm::DenseMap, StructType*> StructTypes; llvm::DenseMap, ClassType*> ClassTypes; llvm::DenseMap, ProtocolType*> ProtocolTypes; + llvm::DenseMap ExistentialTypes; llvm::FoldingSet UnboundGenericTypes; llvm::FoldingSet BoundGenericTypes; llvm::FoldingSet ProtocolCompositionTypes; @@ -4098,6 +4099,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() && !objectTy->is() && "cannot have 'inout' or @lvalue wrapped inside an @lvalue"); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index f91df702e50..5ae1f269106 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3933,6 +3933,13 @@ namespace { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitExistentialType(ExistentialType *T, + StringRef label) { + printCommon(label, "existential_type"); + printRec(T->getConstraintType()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitLValueType(LValueType *T, StringRef label) { printCommon(label, "lvalue_type"); printRec(T->getObjectType()); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 318bcf7bb4a..3bdbdc7af9e 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1251,6 +1251,11 @@ void ASTMangler::appendType(Type type, GenericSignature sig, return appendExistentialLayout(layout, sig, forDecl); } + case TypeKind::Existential: { + auto constraint = cast(tybase)->getConstraintType(); + return appendType(constraint, sig, forDecl); + } + case TypeKind::UnboundGeneric: case TypeKind::Class: case TypeKind::Enum: diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 0ed06e34fdc..bd14aa3518f 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5329,6 +5329,11 @@ public: } } + void visitExistentialType(ExistentialType *T) { + Printer << "any "; + visit(T->getConstraintType()); + } + void visitLValueType(LValueType *T) { Printer << "@lvalue "; visit(T->getObjectType()); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 8aa4844c2da..ffa1b46a963 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1660,6 +1660,12 @@ static void extractDirectlyReferencedNominalTypes( return; } + if (auto existential = type->getAs()) { + extractDirectlyReferencedNominalTypes( + existential->getConstraintType(), decls); + return; + } + llvm_unreachable("Not a type containing nominal types?"); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d9dc6466d3b..da01d694eec 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -190,6 +190,9 @@ bool CanType::isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig, return cast(type)->requiresClass(); case TypeKind::ProtocolComposition: return cast(type)->requiresClass(); + case TypeKind::Existential: + return isReferenceTypeImpl(cast(type).getConstraintType(), + sig, functionsCount); case TypeKind::UnboundGeneric: return isa(cast(type)->getDecl()); @@ -293,6 +296,12 @@ ExistentialLayout TypeBase::getExistentialLayout() { } ExistentialLayout CanType::getExistentialLayout() { + if (auto existential = dyn_cast(*this)) + return existential->getConstraintType()->getExistentialLayout(); + + if (auto metatype = dyn_cast(*this)) + return metatype->getInstanceType()->getExistentialLayout(); + if (auto proto = dyn_cast(*this)) return ExistentialLayout(proto); @@ -1443,6 +1452,12 @@ CanType TypeBase::computeCanonicalType() { Result = Composition.getPointer(); break; } + case TypeKind::Existential: { + auto *existential = cast(this); + auto constraint = existential->getConstraintType()->getCanonicalType(); + Result = ExistentialType::get(constraint); + break; + } case TypeKind::ExistentialMetatype: { auto metatype = cast(this); auto instanceType = metatype->getInstanceType()->getCanonicalType(); @@ -5236,6 +5251,19 @@ case TypeKind::Id: *this : InOutType::get(objectTy); } + case TypeKind::Existential: { + auto *existential = cast(base); + auto constraint = existential->getConstraintType().transformRec(fn); + if (!constraint || constraint->hasError()) + return constraint; + + if (constraint.getPointer() == + existential->getConstraintType().getPointer()) + return *this; + + return ExistentialType::get(constraint); + } + case TypeKind::ProtocolComposition: { auto pc = cast(base); SmallVector substMembers; @@ -5425,6 +5453,10 @@ ReferenceCounting TypeBase::getReferenceCounting() { return ReferenceCounting::Unknown; } + case TypeKind::Existential: + return cast(type)->getConstraintType() + ->getReferenceCounting(); + case TypeKind::Function: case TypeKind::GenericFunction: case TypeKind::SILFunction: diff --git a/lib/AST/TypeWalker.cpp b/lib/AST/TypeWalker.cpp index a6ce37ffdf3..966458f12f2 100644 --- a/lib/AST/TypeWalker.cpp +++ b/lib/AST/TypeWalker.cpp @@ -163,6 +163,10 @@ class Traversal : public TypeVisitor return false; } + bool visitExistentialType(ExistentialType *ty) { + return doIt(ty->getConstraintType()); + } + bool visitLValueType(LValueType *ty) { return doIt(ty->getObjectType()); } diff --git a/lib/IRGen/Fulfillment.cpp b/lib/IRGen/Fulfillment.cpp index 6ea58a82bf2..c6077a06663 100644 --- a/lib/IRGen/Fulfillment.cpp +++ b/lib/IRGen/Fulfillment.cpp @@ -99,6 +99,10 @@ static bool isLeafTypeMetadata(CanType type) { case TypeKind::ProtocolComposition: return false; + // Existential types have constraint types. + case TypeKind::Existential: + return false; + // Metatypes have instance types. case TypeKind::Metatype: case TypeKind::ExistentialMetatype: diff --git a/lib/IRGen/GenExistential.cpp b/lib/IRGen/GenExistential.cpp index 730ea78693f..516eb991368 100644 --- a/lib/IRGen/GenExistential.cpp +++ b/lib/IRGen/GenExistential.cpp @@ -1441,6 +1441,9 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) { } // Note: Protocol composition types are not nominal, but we name them anyway. + if (auto existential = T->getAs()) { + T = existential->getConstraintType()->getCanonicalType(); + } llvm::StructType *type; if (isa(T)) type = IGM.createNominalType(T); @@ -1553,6 +1556,11 @@ TypeConverter::convertProtocolCompositionType(ProtocolCompositionType *T) { return createExistentialTypeInfo(IGM, CanType(T)); } +const TypeInfo * +TypeConverter::convertExistentialType(ExistentialType *T) { + return createExistentialTypeInfo(IGM, CanType(T)); +} + const TypeInfo * TypeConverter::convertExistentialMetatypeType(ExistentialMetatypeType *T) { assert(T->hasRepresentation() && diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 41ab9f632e2..ad9709ed16d 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -2183,6 +2183,8 @@ const TypeInfo *TypeConverter::convertType(CanType ty) { return convertProtocolType(cast(ty)); case TypeKind::ProtocolComposition: return convertProtocolCompositionType(cast(ty)); + case TypeKind::Existential: + return convertExistentialType(cast(ty)); case TypeKind::GenericTypeParam: case TypeKind::DependentMember: llvm_unreachable("can't convert dependent type"); diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index e070dcef0b4..284c74598dc 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -163,6 +163,7 @@ private: const TypeInfo *convertModuleType(ModuleType *T); const TypeInfo *convertProtocolType(ProtocolType *T); const TypeInfo *convertProtocolCompositionType(ProtocolCompositionType *T); + const TypeInfo *convertExistentialType(ExistentialType *T); const LoadableTypeInfo *convertBuiltinNativeObject(); const LoadableTypeInfo *convertBuiltinUnknownObject(); const LoadableTypeInfo *convertBuiltinBridgeObject(); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index aa2d8e6b222..e5998782e94 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -1458,6 +1458,7 @@ private: Flags, MangledName); } + case TypeKind::Existential: case TypeKind::ProtocolComposition: { auto *Decl = DbgTy.getDecl(); auto L = getFilenameAndLocation(*this, Decl); diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 8074ef9840f..b2096617bcb 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1730,6 +1730,12 @@ namespace { return emitExistentialTypeMetadata(type, request); } + MetadataResponse + visitExistentialType(CanExistentialType type, + DynamicMetadataRequest request) { + return emitExistentialTypeMetadata(type, request); + } + MetadataResponse visitReferenceStorageType(CanReferenceStorageType type, DynamicMetadataRequest request) { llvm_unreachable("reference storage type should have been converted by " diff --git a/lib/PrintAsObjC/DeclAndTypePrinter.cpp b/lib/PrintAsObjC/DeclAndTypePrinter.cpp index 4bdd547483b..c552363288a 100644 --- a/lib/PrintAsObjC/DeclAndTypePrinter.cpp +++ b/lib/PrintAsObjC/DeclAndTypePrinter.cpp @@ -1833,6 +1833,12 @@ private: visitExistentialType(PCT, optionalKind, /*isMetatype=*/false); } + void visitExistentialType(ExistentialType *ET, + Optional optionalKind) { + visitExistentialType(ET, optionalKind, + /*isMetatype=*/ET->getConstraintType()->is()); + } + void visitExistentialMetatypeType(ExistentialMetatypeType *MT, Optional optionalKind) { Type instanceTy = MT->getInstanceType(); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 189d94061f2..1edc8ff1e47 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6942,6 +6942,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, case TypeKind::Struct: case TypeKind::Protocol: case TypeKind::ProtocolComposition: + case TypeKind::Existential: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: case TypeKind::GenericFunction: @@ -6954,6 +6955,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto desugaredToType = toType->getDesugaredType(); switch (desugaredToType->getKind()) { // Coercions from a type to an existential type. + case TypeKind::Existential: case TypeKind::ExistentialMetatype: case TypeKind::ProtocolComposition: case TypeKind::Protocol: diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4110d527b8b..b53a522f376 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2920,7 +2920,13 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, // Handle existential metatypes. if (auto meta1 = type1->getAs()) { - if (auto meta2 = type2->getAs()) { + ExistentialMetatypeType *meta2; + if (auto existential = type2->getAs()) { + meta2 = existential->getConstraintType()->getAs(); + } else { + meta2 = type2->getAs(); + } + if (meta2) { return matchExistentialTypes(meta1->getInstanceType(), meta2->getInstanceType(), kind, subflags, locator.withPathElement( @@ -5674,6 +5680,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case TypeKind::GenericFunction: llvm_unreachable("Polymorphic function type should have been opened"); + case TypeKind::Existential: case TypeKind::ProtocolComposition: switch (kind) { case ConstraintKind::Equal: @@ -6318,6 +6325,7 @@ ConstraintSystem::simplifyConstructionConstraint( case TypeKind::DynamicSelf: case TypeKind::ProtocolComposition: case TypeKind::Protocol: + case TypeKind::Existential: // Break out to handle the actual construction below. break; @@ -10694,6 +10702,11 @@ getDynamicCallableMethods(Type type, ConstraintSystem &CS, if (auto protocolComp = dyn_cast(canType)) return calculateForComponentTypes(protocolComp->getMembers()); + if (auto existential = dyn_cast(canType)) { + auto constraint = existential->getConstraintType(); + return getDynamicCallableMethods(constraint, CS, locator); + } + // Otherwise, this must be a nominal type. // Dynamic calling doesn't work for tuples, etc. auto nominal = canType->getAnyNominal(); diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index f4a91fd6f20..a67ee4c7864 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -669,6 +669,13 @@ static void lookupVisibleMemberDeclsImpl( return; } + if (auto *existential = BaseTy->getAs()) { + auto constraint = existential->getConstraintType(); + lookupVisibleMemberDeclsImpl(constraint, Consumer, CurrDC, LS, Reason, + Sig, Visited); + return; + } + // Enumerate members of archetype's requirements. if (ArchetypeType *Archetype = BaseTy->getAs()) { for (auto Proto : Archetype->getConformsTo()) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index e1e5631d11a..172f439b96b 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1834,6 +1834,8 @@ namespace { TypeResolutionOptions options); NeverNullType resolveCompositionType(CompositionTypeRepr *repr, TypeResolutionOptions options); + NeverNullType resolveExistentialType(ExistentialTypeRepr *repr, + TypeResolutionOptions options); NeverNullType resolveMetatypeType(MetatypeTypeRepr *repr, TypeResolutionOptions options); NeverNullType resolveProtocolType(ProtocolTypeRepr *repr, @@ -3723,6 +3725,32 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, /*HasExplicitAnyObject=*/false); } +NeverNullType +TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, + TypeResolutionOptions options) { + auto constraintType = resolveType(repr->getConstraint(), options); + + auto anyStart = repr->getAnyLoc(); + auto anyEnd = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, anyStart); + if (!constraintType->isExistentialType() && + !constraintType->is()) { + diagnose(repr->getLoc(), diag::any_not_existential, + constraintType->isTypeParameter(), + constraintType) + .fixItRemove({anyStart, anyEnd}); + return constraintType; + } + + // Warn about `any Any` and `any AnyObject`. + if (constraintType->isAny() || constraintType->isAnyObject()) { + diagnose(repr->getLoc(), diag::unnecessary_any, + constraintType) + .fixItRemove({anyStart, anyEnd}); + } + + return ExistentialType::get(constraintType); +} + NeverNullType TypeResolver::resolveMetatypeType(MetatypeTypeRepr *repr, TypeResolutionOptions options) { // The instance type of a metatype is always abstract, not SIL-lowered. diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 52744dbc694..911b31048f1 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -94,6 +94,7 @@ TYPE(OPAQUE_ARCHETYPE) TYPE(NESTED_ARCHETYPE) TYPE(SEQUENCE_ARCHETYPE) TYPE(PROTOCOL_COMPOSITION) +TYPE(EXISTENTIAL) TYPE(BOUND_GENERIC) TYPE(GENERIC_FUNCTION) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index f2c70d45267..76044ef39fe 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5606,6 +5606,18 @@ public: return ProtocolCompositionType::get(ctx, protocols, hasExplicitAnyObject); } + Expected deserializeExistentialType(ArrayRef scratch, + StringRef blobData) { + TypeID constraintID; + decls_block::ExistentialTypeLayout::readRecord(scratch, constraintID); + + auto constraintType = MF.getTypeChecked(constraintID); + if (!constraintType) + return constraintType.takeError(); + + return ExistentialType::get(constraintType.get()); + } + Expected deserializeDependentMemberType(ArrayRef scratch, StringRef blobData) { TypeID baseID; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 4a03d407dd1..2284f683bfd 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 649; // _unavailableFromAsync message +const uint16_t SWIFTMODULE_VERSION_MINOR = 650; // model ExistentialType /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1184,6 +1184,8 @@ namespace decls_block { using ArraySliceTypeLayout = SyntaxSugarTypeLayout; using OptionalTypeLayout = SyntaxSugarTypeLayout; using VariadicSequenceTypeLayout = SyntaxSugarTypeLayout; + using ExistentialTypeLayout = + SyntaxSugarTypeLayout; using DictionaryTypeLayout = BCRecordLayout< DICTIONARY_TYPE, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 22fb26c451e..083bc65fd4a 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4706,6 +4706,12 @@ public: protocols); } + void + visitExistentialType(const ExistentialType *existential) { + using namespace decls_block; + serializeSimpleWrapper(existential->getConstraintType()); + } + void visitReferenceStorageType(const ReferenceStorageType *refTy) { using namespace decls_block; unsigned abbrCode = S.DeclTypeAbbrCodes[ReferenceStorageTypeLayout::Code]; @@ -4882,6 +4888,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); From 76906304f7e6ca92674ec210640e19877d1c9c30 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 21 Nov 2021 18:55:24 -0500 Subject: [PATCH 04/16] [TypeResolver] Resolve existential types to ExistentialType when explicit existential types are enabled. Note that existential metatypes still resolve to ExistentialMetatypeType, but later this type can be replaced with ExistentialType(MetatypeType). --- lib/AST/ASTPrinter.cpp | 4 ++- lib/Sema/TypeCheckGeneric.cpp | 11 +++++-- lib/Sema/TypeCheckType.cpp | 46 ++++++++++++++++++++++------- lib/Sema/TypeCheckType.h | 54 +++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 14 deletions(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index bd14aa3518f..9944e6c9a91 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4742,8 +4742,10 @@ public: printWithParensIfNotSimple(T->getInstanceType()); // We spell normal metatypes of existential types as .Protocol. + auto &ctx = T->getASTContext(); if (isa(T) && - T->getInstanceType()->isAnyExistentialType()) { + T->getInstanceType()->isAnyExistentialType() && + !ctx.LangOpts.EnableExplicitExistentialTypes) { Printer << ".Protocol"; } else { Printer << ".Type"; diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 3d514c7bc04..9ed28d55320 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -962,8 +962,16 @@ RequirementRequest::evaluate(Evaluator &evaluator, WhereClauseOwner owner, unsigned index, TypeResolutionStage stage) const { + auto &reqRepr = getRequirement(); + // Figure out the type resolution. - auto options = TypeResolutionOptions(TypeResolverContext::GenericRequirement); + TypeResolverContext context; + if (reqRepr.getKind() == RequirementReprKind::SameType) { + context = TypeResolverContext::SameTypeRequirement; + } else { + context = TypeResolverContext::GenericRequirement; + } + auto options = TypeResolutionOptions(context); if (owner.dc->isInSpecializeExtensionContext()) options |= TypeResolutionFlags::AllowUsableFromInline; Optional resolution; @@ -981,7 +989,6 @@ RequirementRequest::evaluate(Evaluator &evaluator, break; } - auto &reqRepr = getRequirement(); switch (reqRepr.getKind()) { case RequirementReprKind::TypeConstraint: { Type subject = resolution->resolveType(reqRepr.getSubjectRepr()); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 172f439b96b..785a5f005c7 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1931,7 +1931,8 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, // Strip the "is function input" bits unless this is a type that knows about // them. - if (!isa(repr) && !isa(repr) && + if (options.is(TypeResolverContext::FunctionInput) && + !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr)) { @@ -2049,7 +2050,7 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, } auto *existential = cast(repr); - return resolveType(existential->getConstraint(), options); + return resolveExistentialType(existential, options); } case TypeReprKind::NamedOpaqueReturn: @@ -3363,6 +3364,14 @@ TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, return ErrorType::get(getASTContext()); } + bool isConstraintType = (result->is() || + result->is()); + if (isConstraintType && + getASTContext().LangOpts.EnableExplicitExistentialTypes && + options.isConstraintImplicitExistential()) { + return ExistentialType::get(result); + } + // Hack to apply context-specific @escaping to a typealias with an underlying // function type. if (result->is()) @@ -3542,6 +3551,9 @@ NeverNullType TypeResolver::resolveImplicitlyUnwrappedOptionalType( case TypeResolverContext::TypeAliasDecl: case TypeResolverContext::GenericTypeAliasDecl: case TypeResolverContext::GenericRequirement: + case TypeResolverContext::SameTypeRequirement: + case TypeResolverContext::ProtocolMetatypeBase: + case TypeResolverContext::MetatypeBase: case TypeResolverContext::ImmediateOptionalTypeArgument: case TypeResolverContext::InExpression: case TypeResolverContext::EditorPlaceholderExpr: @@ -3684,7 +3696,8 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, }; for (auto tyR : repr->getTypes()) { - auto ty = resolveType(tyR, options.withoutContext()); + auto ty = resolveType(tyR, + options.withContext(TypeResolverContext::GenericRequirement)); if (ty->hasError()) return ty; auto nominalDecl = ty->getAnyNominal(); @@ -3721,19 +3734,27 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, // In user-written types, AnyObject constraints always refer to the // AnyObject type in the standard library. - return ProtocolCompositionType::get(getASTContext(), Members, - /*HasExplicitAnyObject=*/false); + auto composition = + ProtocolCompositionType::get(getASTContext(), Members, + /*HasExplicitAnyObject=*/false); + if (getASTContext().LangOpts.EnableExplicitExistentialTypes && + options.isConstraintImplicitExistential()) { + composition = ExistentialType::get(composition); + } + return composition; } NeverNullType TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, TypeResolutionOptions options) { - auto constraintType = resolveType(repr->getConstraint(), options); + auto constraintType = resolveType(repr->getConstraint(), + options.withContext(TypeResolverContext::GenericRequirement)); + if (constraintType->is()) + return constraintType; auto anyStart = repr->getAnyLoc(); auto anyEnd = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, anyStart); - if (!constraintType->isExistentialType() && - !constraintType->is()) { + if (!constraintType->isExistentialType()) { diagnose(repr->getLoc(), diag::any_not_existential, constraintType->isTypeParameter(), constraintType) @@ -3754,7 +3775,8 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, NeverNullType TypeResolver::resolveMetatypeType(MetatypeTypeRepr *repr, TypeResolutionOptions options) { // The instance type of a metatype is always abstract, not SIL-lowered. - auto ty = resolveType(repr->getBase(), options.withoutContext()); + auto ty = resolveType(repr->getBase(), + options.withContext(TypeResolverContext::MetatypeBase)); if (ty->hasError()) { return ErrorType::get(getASTContext()); } @@ -3775,7 +3797,8 @@ NeverNullType TypeResolver::resolveMetatypeType(MetatypeTypeRepr *repr, NeverNullType TypeResolver::buildMetatypeType(MetatypeTypeRepr *repr, Type instanceType, Optional storedRepr) { - if (instanceType->isAnyExistentialType()) { + if (instanceType->isAnyExistentialType() && + !instanceType->is()) { // TODO: diagnose invalid representations? return ExistentialMetatypeType::get(instanceType, storedRepr); } else { @@ -3786,7 +3809,8 @@ TypeResolver::buildMetatypeType(MetatypeTypeRepr *repr, Type instanceType, NeverNullType TypeResolver::resolveProtocolType(ProtocolTypeRepr *repr, TypeResolutionOptions options) { // The instance type of a metatype is always abstract, not SIL-lowered. - auto ty = resolveType(repr->getBase(), options.withoutContext()); + auto ty = resolveType(repr->getBase(), + options.withContext(TypeResolverContext::ProtocolMetatypeBase)); if (ty->hasError()) { return ErrorType::get(getASTContext()); } diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index f5c9ea23a17..a34aecf381d 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -125,6 +125,16 @@ enum class TypeResolverContext : uint8_t { /// Whether we are in a requirement of a generic declaration GenericRequirement, + /// Whether we are in a same-type requirement of a generic + /// declaration. + SameTypeRequirement, + + /// Whether this is the base type of .Protocol + ProtocolMetatypeBase, + + /// Whether this is the base type of .Type + MetatypeBase, + /// Whether we are in a type argument for an optional ImmediateOptionalTypeArgument, @@ -215,6 +225,9 @@ public: case Context::TypeAliasDecl: case Context::GenericTypeAliasDecl: case Context::GenericRequirement: + case Context::SameTypeRequirement: + case Context::ProtocolMetatypeBase: + case Context::MetatypeBase: case Context::ImmediateOptionalTypeArgument: case Context::AbstractFunctionDecl: case Context::Inherited: @@ -224,6 +237,40 @@ public: llvm_unreachable("unhandled kind"); } + /// Whether a generic constraint type is implicitly an + /// existential type in this context. + bool isConstraintImplicitExistential() const { + switch (context) { + case Context::Inherited: + case Context::ExtensionBinding: + case Context::TypeAliasDecl: + case Context::GenericTypeAliasDecl: + case Context::GenericRequirement: + case Context::MetatypeBase: + return false; + case Context::None: + case Context::InExpression: + case Context::ExplicitCastExpr: + case Context::ForEachStmt: + case Context::PatternBindingDecl: + case Context::EditorPlaceholderExpr: + case Context::ClosureExpr: + case Context::FunctionInput: + case Context::VariadicFunctionInput: + case Context::InoutFunctionInput: + case Context::FunctionResult: + case Context::SubscriptDecl: + case Context::EnumElementDecl: + case Context::EnumPatternPayload: + case Context::SameTypeRequirement: + case Context::ProtocolMetatypeBase: + case Context::ImmediateOptionalTypeArgument: + case Context::AbstractFunctionDecl: + case Context::CustomAttr: + return true; + } + } + /// Determine whether all of the given options are set. bool contains(TypeResolutionFlags set) const { return !static_cast(unsigned(set) & ~unsigned(flags)); @@ -276,6 +323,13 @@ public: if (!preserveSIL) copy -= TypeResolutionFlags::SILType; return copy; } + + inline + TypeResolutionOptions withContext(TypeResolverContext context) const { + auto copy = *this; + copy.setContext(context); + return copy; + } }; /// A function reference used to "open" the given unbound generic type From 00a462951575e5aa98f7de4b7e990218e07e6608 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 11 Nov 2021 13:48:51 -0800 Subject: [PATCH 05/16] Revert "NFC: Remove the now dead ProtocolDecl::existentialTypeSupported()" This reverts commit eb1bd07bb36f08c1c7651d8eace3b4c5924c8d11. --- include/swift/AST/Decl.h | 30 ++++++++++++++++++++- include/swift/AST/TypeCheckRequests.h | 26 ++++++++++++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 ++ lib/AST/Decl.cpp | 5 ++++ lib/AST/TypeCheckRequests.cpp | 25 +++++++++++++++++ lib/Sema/TypeCheckDecl.cpp | 28 +++++++++++++++++++ lib/Serialization/Deserialization.cpp | 5 +++- lib/Serialization/ModuleFormat.h | 1 + lib/Serialization/Serialization.cpp | 1 + 9 files changed, 121 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 4f121656993..1feb7029095 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -520,7 +520,7 @@ protected: 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, @@ -533,6 +533,12 @@ protected: /// 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, @@ -4244,6 +4250,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 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; } @@ -4257,6 +4278,7 @@ class ProtocolDecl final : public NominalTypeDecl { friend class RequirementSignatureRequestRQM; friend class ProtocolRequiresClassRequest; friend class ExistentialConformsToSelfRequest; + friend class ExistentialTypeSupportedRequest; friend class InheritedProtocolsRequest; public: @@ -4345,6 +4367,12 @@ public: /// 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( diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 4f516ff6997..3432f13c181 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -287,6 +287,32 @@ public: void cacheResult(bool value) const; }; +/// Determine whether we are allowed to refer to an existential type conforming +/// to this protocol. +class ExistentialTypeSupportedRequest : + public SimpleRequest { +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 getCachedResult() const; + void cacheResult(bool value) const; +}; + class PolymorphicEffectRequirementsRequest : public SimpleRequest(this)}, true); +} + StringRef ProtocolDecl::getObjCRuntimeName( llvm::SmallVectorImpl &buffer) const { // If there is an 'objc' attribute with a name, use that name. diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 2471e9ddc67..82a51fc8420 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -249,6 +249,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 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. //----------------------------------------------------------------------------// diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 5d1de0900a6..e84c1a13327 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -666,6 +666,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(member)) + return false; + + // For value members, look at their type signatures. + if (auto valueMember = dyn_cast(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(decl)) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 76044ef39fe..b9d827fc412 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3564,13 +3564,14 @@ public: StringRef blobData) { IdentifierID nameID; DeclContextID contextID; - bool isImplicit, isClassBounded, isObjC; + bool isImplicit, isClassBounded, isObjC, existentialTypeSupported; uint8_t rawAccessLevel; unsigned numInheritedTypes; ArrayRef rawInheritedAndDependencyIDs; decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID, isImplicit, isClassBounded, isObjC, + existentialTypeSupported, rawAccessLevel, numInheritedTypes, rawInheritedAndDependencyIDs); @@ -3596,6 +3597,8 @@ public: ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto}, std::move(isClassBounded)); + ctx.evaluator.cacheOutput(ExistentialTypeSupportedRequest{proto}, + std::move(existentialTypeSupported)); if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) proto->setAccess(*accessLevel); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 2284f683bfd..15683309a63 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1292,6 +1292,7 @@ namespace decls_block { BCFixed<1>, // implicit flag BCFixed<1>, // class-bounded? BCFixed<1>, // objc? + BCFixed<1>, // existential-type-supported? AccessLevelField, // access level BCVBR<4>, // number of inherited types BCArray // inherited types, followed by dependency types diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 083bc65fd4a..345e2b7824f 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3630,6 +3630,7 @@ public: const_cast(proto) ->requiresClass(), proto->isObjC(), + proto->existentialTypeSupported(), rawAccessLevel, numInherited, inheritedAndDependencyTypes); From 7c7b3ba0da5557109fcd689ea02fa73446258a76 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 11 Nov 2021 14:32:50 -0800 Subject: [PATCH 06/16] Revert "[NFC, Typechecker] Remove UnsupportedProtocolVisitor and checkUnsupportedProtocolType()" This reverts commit 15f88e9be3ad84b4c9831975a2acb1f1ddc442de. --- lib/Sema/MiscDiagnostics.cpp | 4 + lib/Sema/TypeCheckDeclPrimary.cpp | 2 + lib/Sema/TypeCheckType.cpp | 122 ++++++++++++++++++++++++++++++ lib/Sema/TypeChecker.h | 16 ++++ 4 files changed, 144 insertions(+) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index dc3ef7e334c..1b5fa2b92df 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -3361,6 +3361,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. @@ -4839,6 +4841,8 @@ void swift::performSyntacticExprDiagnostics(const Expr *E, void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { auto &ctx = DC->getASTContext(); + TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); + if (auto switchStmt = dyn_cast(S)) checkSwitch(ctx, switchStmt); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 4c9556bf724..7cf22d9568b 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1696,6 +1696,8 @@ public: DeclVisitor::visit(decl); + TypeChecker::checkUnsupportedProtocolType(decl); + if (auto VD = dyn_cast(decl)) { auto &Context = getASTContext(); TypeChecker::checkForForbiddenPrefix(Context, VD->getBaseName()); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 785a5f005c7..cda0c2abb47 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3925,6 +3925,128 @@ Type TypeChecker::substMemberTypeWithBase(ModuleDecl *module, return resultType; } +namespace { + +class UnsupportedProtocolVisitor + : public TypeReprVisitor, public ASTWalker +{ + ASTContext &Ctx; + bool checkStatements; + bool hitTopStmt; + +public: + UnsupportedProtocolVisitor(ASTContext &ctx, bool checkStatements) + : Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false) { } + + bool walkToTypeReprPre(TypeRepr *T) override { + if (T->isInvalid()) + return false; + if (auto compound = dyn_cast(T)) { + // Only visit the last component to check, because nested typealiases in + // existentials are okay. + visit(compound->getComponentRange().back()); + return false; + } + // Arbitrary protocol constraints are OK on opaque types. + if (isa(T)) + return false; + + visit(T); + return true; + } + + std::pair walkToStmtPre(Stmt *S) override { + if (checkStatements && !hitTopStmt) { + hitTopStmt = true; + return { true, S }; + } + + return { false, S }; + } + + bool walkToDeclPre(Decl *D) override { + return !checkStatements; + } + + void visitTypeRepr(TypeRepr *T) { + // Do nothing for all TypeReprs except the ones listed below. + } + + void visitIdentTypeRepr(IdentTypeRepr *T) { + return; + } + + void visitRequirements(ArrayRef reqts) { + for (auto reqt : reqts) { + if (reqt.getKind() == RequirementReprKind::SameType) { + if (auto *repr = reqt.getFirstTypeRepr()) + repr->walk(*this); + if (auto *repr = reqt.getSecondTypeRepr()) + repr->walk(*this); + } + } + } +}; + +} // end anonymous namespace + +void TypeChecker::checkUnsupportedProtocolType(Decl *decl) { + if (!decl || decl->isInvalid()) + return; + + auto &ctx = decl->getASTContext(); + if (auto *protocolDecl = dyn_cast(decl)) { + checkUnsupportedProtocolType(ctx, protocolDecl->getTrailingWhereClause()); + } else if (auto *genericDecl = dyn_cast(decl)) { + checkUnsupportedProtocolType(ctx, genericDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, genericDecl->getTrailingWhereClause()); + } else if (auto *assocType = dyn_cast(decl)) { + checkUnsupportedProtocolType(ctx, assocType->getTrailingWhereClause()); + } else if (auto *extDecl = dyn_cast(decl)) { + checkUnsupportedProtocolType(ctx, extDecl->getTrailingWhereClause()); + } else if (auto *subscriptDecl = dyn_cast(decl)) { + checkUnsupportedProtocolType(ctx, subscriptDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, subscriptDecl->getTrailingWhereClause()); + } else if (auto *funcDecl = dyn_cast(decl)) { + if (!isa(funcDecl)) { + checkUnsupportedProtocolType(ctx, funcDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, funcDecl->getTrailingWhereClause()); + } + } + + if (isa(decl) || isa(decl)) + return; + + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + decl->walk(visitor); +} + +void TypeChecker::checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt) { + if (!stmt) + return; + + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/true); + stmt->walk(visitor); +} + +void TypeChecker::checkUnsupportedProtocolType( + ASTContext &ctx, TrailingWhereClause *whereClause) { + if (whereClause == nullptr) + return; + + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + visitor.visitRequirements(whereClause->getRequirements()); +} + +void TypeChecker::checkUnsupportedProtocolType( + ASTContext &ctx, GenericParamList *genericParams) { + if (genericParams == nullptr) + return; + + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + visitor.visitRequirements(genericParams->getRequirements()); +} + Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr, DeclContext *dc, CustomAttrTypeKind typeKind) const { diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 057a2f859f0..032255314f8 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -245,6 +245,22 @@ Type getOptionalType(SourceLoc loc, Type elementType); Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context, bool replaceInvalidRefsWithErrors); +/// Check for unsupported protocol types in the given declaration. +void checkUnsupportedProtocolType(Decl *decl); + +/// Check for unsupported protocol types in the given statement. +void checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt); + +/// Check for unsupported protocol types in the given generic requirement +/// list. +void checkUnsupportedProtocolType(ASTContext &ctx, + TrailingWhereClause *whereClause); + +/// Check for unsupported protocol types in the given generic requirement +/// list. +void checkUnsupportedProtocolType(ASTContext &ctx, + GenericParamList *genericParams); + /// Substitute the given base type into the type of the given nested type, /// producing the effective type that the nested type will have. /// From 260654cd33dba78f909893056fc6ca661a314cb1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 12 Nov 2021 17:42:06 -0800 Subject: [PATCH 07/16] [Serialization] Bump the module format version for existential `any` serialization. --- lib/Serialization/ModuleFormat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 15683309a63..451cc8f30a6 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 650; // model ExistentialType +const uint16_t SWIFTMODULE_VERSION_MINOR = 651; // existential requires any /// A standard hash seed used for all string hashes in a serialized module. /// From 37c0964d3e56b8fab88e29ecdca1e9715df0a0d0 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 17 Nov 2021 19:57:22 -0800 Subject: [PATCH 08/16] [Sema] Rename existentialTypeSupported to existentialRequiresAny, and flip the return value in the implementation accordingly. --- include/swift/AST/Decl.h | 35 ++++++++++----------- include/swift/AST/TypeCheckRequests.h | 8 ++--- include/swift/AST/TypeCheckerTypeIDZone.def | 2 +- lib/AST/Decl.cpp | 4 +-- lib/AST/TypeCheckRequests.cpp | 14 ++++----- lib/Sema/TypeCheckDecl.cpp | 22 ++++++------- lib/Serialization/Deserialization.cpp | 8 ++--- lib/Serialization/Serialization.cpp | 2 +- 8 files changed, 47 insertions(+), 48 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 1feb7029095..7114980a6d2 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -533,11 +533,11 @@ protected: /// Whether the existential of this protocol conforms to itself. ExistentialConformsToSelf : 1, - /// Whether the \c ExistentialTypeSupported bit is valid. - ExistentialTypeSupportedValid : 1, + /// Whether the \c ExistentialRequiresAny bit is valid. + ExistentialRequiresAnyValid : 1, - /// Whether the existential of this protocol can be represented. - ExistentialTypeSupported : 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). @@ -4250,19 +4250,19 @@ class ProtocolDecl final : public NominalTypeDecl { Bits.ProtocolDecl.ExistentialConformsToSelf = result; } - /// Returns the cached result of \c existentialTypeSupported or \c None if it + /// Returns the cached result of \c existentialRequiresAny or \c None if it /// hasn't yet been computed. - Optional getCachedExistentialTypeSupported() { - if (Bits.ProtocolDecl.ExistentialTypeSupportedValid) - return Bits.ProtocolDecl.ExistentialTypeSupported; + Optional getCachedExistentialRequiresAny() { + if (Bits.ProtocolDecl.ExistentialRequiresAnyValid) + return Bits.ProtocolDecl.ExistentialRequiresAny; return None; } - /// Caches the result of \c existentialTypeSupported - void setCachedExistentialTypeSupported(bool supported) { - Bits.ProtocolDecl.ExistentialTypeSupportedValid = true; - Bits.ProtocolDecl.ExistentialTypeSupported = supported; + /// Caches the result of \c existentialRequiresAny + void setCachedExistentialRequiresAny(bool requiresAny) { + Bits.ProtocolDecl.ExistentialRequiresAnyValid = true; + Bits.ProtocolDecl.ExistentialRequiresAny = requiresAny; } bool hasLazyRequirementSignature() const { @@ -4278,7 +4278,7 @@ class ProtocolDecl final : public NominalTypeDecl { friend class RequirementSignatureRequestRQM; friend class ProtocolRequiresClassRequest; friend class ExistentialConformsToSelfRequest; - friend class ExistentialTypeSupportedRequest; + friend class ExistentialRequiresAnyRequest; friend class InheritedProtocolsRequest; public: @@ -4367,11 +4367,10 @@ public: /// 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; + /// 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. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 3432f13c181..c903a86a5cd 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -287,10 +287,10 @@ public: void cacheResult(bool value) const; }; -/// Determine whether we are allowed to refer to an existential type conforming -/// to this protocol. -class ExistentialTypeSupportedRequest : - public SimpleRequest { public: diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 1022f4a738c..34120d856e9 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -92,7 +92,7 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest, Type(EnumDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest, +SWIFT_REQUEST(TypeChecker, ExistentialRequiresAnyRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached, NoLocationInfo) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 364f558f8fa..872529486fa 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5266,9 +5266,9 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { return true; } -bool ProtocolDecl::existentialTypeSupported() const { +bool ProtocolDecl::existentialRequiresAny() const { return evaluateOrDefault(getASTContext().evaluator, - ExistentialTypeSupportedRequest{const_cast(this)}, true); + ExistentialRequiresAnyRequest{const_cast(this)}, true); } StringRef ProtocolDecl::getObjCRuntimeName( diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 82a51fc8420..b1dd853686a 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -250,28 +250,28 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const { } //----------------------------------------------------------------------------// -// existentialTypeSupported computation. +// existentialRequiresAny computation. //----------------------------------------------------------------------------// -void ExistentialTypeSupportedRequest::diagnoseCycle(DiagnosticEngine &diags) const { +void ExistentialRequiresAnyRequest::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 { +void ExistentialRequiresAnyRequest::noteCycleStep(DiagnosticEngine &diags) const { auto requirement = std::get<0>(getStorage()); diags.diagnose(requirement, diag::kind_declname_declared_here, DescriptiveDeclKind::Protocol, requirement->getName()); } -Optional ExistentialTypeSupportedRequest::getCachedResult() const { +Optional ExistentialRequiresAnyRequest::getCachedResult() const { auto decl = std::get<0>(getStorage()); - return decl->getCachedExistentialTypeSupported(); + return decl->getCachedExistentialRequiresAny(); } -void ExistentialTypeSupportedRequest::cacheResult(bool value) const { +void ExistentialRequiresAnyRequest::cacheResult(bool value) const { auto decl = std::get<0>(getStorage()); - decl->setCachedExistentialTypeSupported(value); + decl->setCachedExistentialRequiresAny(value); } //----------------------------------------------------------------------------// diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index e84c1a13327..48cf3f8520e 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -667,31 +667,31 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator, } bool -ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator, - ProtocolDecl *decl) const { - // ObjC protocols can always be existential. +ExistentialRequiresAnyRequest::evaluate(Evaluator &evaluator, + ProtocolDecl *decl) const { + // ObjC protocols do not require `any`. if (decl->isObjC()) - return true; + return false; for (auto member : decl->getMembers()) { - // Existential types cannot be used if the protocol has an associated type. + // Existential types require `any` if the protocol has an associated type. if (isa(member)) - return false; + return true; // For value members, look at their type signatures. if (auto valueMember = dyn_cast(member)) { if (!decl->isAvailableInExistential(valueMember)) - return false; + return true; } } - // Check whether all of the inherited protocols support existential types. + // Check whether any of the inherited protocols require `any`. for (auto proto : decl->getInheritedProtocols()) { - if (!proto->existentialTypeSupported()) - return false; + if (proto->existentialRequiresAny()) + return true; } - return true; + return false; } bool diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index b9d827fc412..2dbc332c86c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3564,14 +3564,14 @@ public: StringRef blobData) { IdentifierID nameID; DeclContextID contextID; - bool isImplicit, isClassBounded, isObjC, existentialTypeSupported; + bool isImplicit, isClassBounded, isObjC, existentialRequiresAny; uint8_t rawAccessLevel; unsigned numInheritedTypes; ArrayRef rawInheritedAndDependencyIDs; decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID, isImplicit, isClassBounded, isObjC, - existentialTypeSupported, + existentialRequiresAny, rawAccessLevel, numInheritedTypes, rawInheritedAndDependencyIDs); @@ -3597,8 +3597,8 @@ public: ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto}, std::move(isClassBounded)); - ctx.evaluator.cacheOutput(ExistentialTypeSupportedRequest{proto}, - std::move(existentialTypeSupported)); + ctx.evaluator.cacheOutput(ExistentialRequiresAnyRequest{proto}, + std::move(existentialRequiresAny)); if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) proto->setAccess(*accessLevel); diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 345e2b7824f..01506ff0427 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3630,7 +3630,7 @@ public: const_cast(proto) ->requiresClass(), proto->isObjC(), - proto->existentialTypeSupported(), + proto->existentialRequiresAny(), rawAccessLevel, numInherited, inheritedAndDependencyTypes); From 8d52f71561d9cbfa9fda34e94444398ca574401f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 17 Nov 2021 22:39:26 -0800 Subject: [PATCH 09/16] [NFC][Sema] Rename checkUnsupportedProtocolType to checkExistentialTypes. --- lib/Sema/MiscDiagnostics.cpp | 4 ++-- lib/Sema/TypeCheckDeclPrimary.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 40 +++++++++++++++---------------- lib/Sema/TypeChecker.h | 20 ++++++++-------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 1b5fa2b92df..2569bde8c4a 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -3361,7 +3361,7 @@ 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); + TypeChecker::checkExistentialTypes(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 @@ -4841,7 +4841,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E, void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { auto &ctx = DC->getASTContext(); - TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); + TypeChecker::checkExistentialTypes(ctx, const_cast(S)); if (auto switchStmt = dyn_cast(S)) checkSwitch(ctx, switchStmt); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 7cf22d9568b..1910948b0b1 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1696,7 +1696,7 @@ public: DeclVisitor::visit(decl); - TypeChecker::checkUnsupportedProtocolType(decl); + TypeChecker::checkExistentialTypes(decl); if (auto VD = dyn_cast(decl)) { auto &Context = getASTContext(); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index cda0c2abb47..510b9fc32db 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3927,15 +3927,15 @@ Type TypeChecker::substMemberTypeWithBase(ModuleDecl *module, namespace { -class UnsupportedProtocolVisitor - : public TypeReprVisitor, public ASTWalker +class ExistentialTypeVisitor + : public TypeReprVisitor, public ASTWalker { ASTContext &Ctx; bool checkStatements; bool hitTopStmt; public: - UnsupportedProtocolVisitor(ASTContext &ctx, bool checkStatements) + ExistentialTypeVisitor(ASTContext &ctx, bool checkStatements) : Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false) { } bool walkToTypeReprPre(TypeRepr *T) override { @@ -3990,60 +3990,60 @@ public: } // end anonymous namespace -void TypeChecker::checkUnsupportedProtocolType(Decl *decl) { +void TypeChecker::checkExistentialTypes(Decl *decl) { if (!decl || decl->isInvalid()) return; auto &ctx = decl->getASTContext(); if (auto *protocolDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, protocolDecl->getTrailingWhereClause()); + checkExistentialTypes(ctx, protocolDecl->getTrailingWhereClause()); } else if (auto *genericDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, genericDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, genericDecl->getTrailingWhereClause()); + checkExistentialTypes(ctx, genericDecl->getGenericParams()); + checkExistentialTypes(ctx, genericDecl->getTrailingWhereClause()); } else if (auto *assocType = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, assocType->getTrailingWhereClause()); + checkExistentialTypes(ctx, assocType->getTrailingWhereClause()); } else if (auto *extDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, extDecl->getTrailingWhereClause()); + checkExistentialTypes(ctx, extDecl->getTrailingWhereClause()); } else if (auto *subscriptDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, subscriptDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, subscriptDecl->getTrailingWhereClause()); + checkExistentialTypes(ctx, subscriptDecl->getGenericParams()); + checkExistentialTypes(ctx, subscriptDecl->getTrailingWhereClause()); } else if (auto *funcDecl = dyn_cast(decl)) { if (!isa(funcDecl)) { - checkUnsupportedProtocolType(ctx, funcDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, funcDecl->getTrailingWhereClause()); + checkExistentialTypes(ctx, funcDecl->getGenericParams()); + checkExistentialTypes(ctx, funcDecl->getTrailingWhereClause()); } } if (isa(decl) || isa(decl)) return; - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/false); decl->walk(visitor); } -void TypeChecker::checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt) { +void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt) { if (!stmt) return; - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/true); + ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/true); stmt->walk(visitor); } -void TypeChecker::checkUnsupportedProtocolType( +void TypeChecker::checkExistentialTypes( ASTContext &ctx, TrailingWhereClause *whereClause) { if (whereClause == nullptr) return; - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/false); visitor.visitRequirements(whereClause->getRequirements()); } -void TypeChecker::checkUnsupportedProtocolType( +void TypeChecker::checkExistentialTypes( ASTContext &ctx, GenericParamList *genericParams) { if (genericParams == nullptr) return; - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); + ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/false); visitor.visitRequirements(genericParams->getRequirements()); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 032255314f8..7b0cc926cff 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -245,21 +245,21 @@ Type getOptionalType(SourceLoc loc, Type elementType); Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context, bool replaceInvalidRefsWithErrors); -/// Check for unsupported protocol types in the given declaration. -void checkUnsupportedProtocolType(Decl *decl); +/// Check for invalid existential types in the given declaration. +void checkExistentialTypes(Decl *decl); -/// Check for unsupported protocol types in the given statement. -void checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt); +/// Check for invalid existential types in the given statement. +void checkExistentialTypes(ASTContext &ctx, Stmt *stmt); -/// Check for unsupported protocol types in the given generic requirement +/// Check for invalid existential types in the given generic requirement /// list. -void checkUnsupportedProtocolType(ASTContext &ctx, - TrailingWhereClause *whereClause); +void checkExistentialTypes(ASTContext &ctx, + TrailingWhereClause *whereClause); -/// Check for unsupported protocol types in the given generic requirement +/// Check for invalid existential types in the given generic requirement /// list. -void checkUnsupportedProtocolType(ASTContext &ctx, - GenericParamList *genericParams); +void checkExistentialTypes(ASTContext &ctx, + GenericParamList *genericParams); /// Substitute the given base type into the type of the given nested type, /// producing the effective type that the nested type will have. From 3e936e3e6718c8ea04f4df6596a12051ce67664c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 18 Nov 2021 23:36:59 -0800 Subject: [PATCH 10/16] [Sema] Require existential types with Self or associated type requirements to be spelled with 'any' when explicit existential types are enabled. --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckType.cpp | 35 ++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 772b6b82f76..9ea910c55aa 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4623,6 +4623,9 @@ WARNING(unnecessary_any,none, 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", diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 510b9fc32db..9b329337216 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3950,6 +3950,10 @@ public: // Arbitrary protocol constraints are OK on opaque types. if (isa(T)) return false; + + // Arbitrary protocol constraints are okay for 'any' types. + if (isa(T)) + return false; visit(T); return true; @@ -3973,7 +3977,36 @@ public: } void visitIdentTypeRepr(IdentTypeRepr *T) { - return; + if (T->isInvalid() || !Ctx.LangOpts.EnableExplicitExistentialTypes) + return; + + auto comp = T->getComponentRange().back(); + if (auto *proto = dyn_cast_or_null(comp->getBoundDecl())) { + if (proto->existentialRequiresAny()) { + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::existential_requires_any, + proto->getName()); + } + } else if (auto *alias = dyn_cast_or_null(comp->getBoundDecl())) { + auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType()); + type.findIf([&](Type type) -> bool { + if (T->isInvalid()) + return false; + if (type->isExistentialType()) { + auto layout = type->getExistentialLayout(); + for (auto *proto : layout.getProtocols()) { + auto *protoDecl = proto->getDecl(); + if (!protoDecl->existentialRequiresAny()) + continue; + + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::existential_requires_any, + protoDecl->getName()); + } + } + return false; + }); + } } void visitRequirements(ArrayRef reqts) { From 9e3c0e6370142ba7c929cd41181295866b5db137 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 18 Nov 2021 23:38:25 -0800 Subject: [PATCH 11/16] [Parse] Parse 'any' as a contextual keyword. --- include/swift/AST/DiagnosticsParse.def | 3 ++- lib/Parse/ParsePattern.cpp | 3 ++- lib/Parse/ParseType.cpp | 27 ++++++++++++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index e95b5d378ad..0ea07eaf3a4 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -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 diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 93fe7175371..162eb5aa82f 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -165,7 +165,8 @@ bool Parser::startsParameterName(bool isClosure) { if (nextTok.canBeArgumentLabel()) { // If the first name wasn't "isolated", we're done. if (!Tok.isContextualKeyword("isolated") && - !Tok.isContextualKeyword("some")) + !Tok.isContextualKeyword("some") && + !Tok.isContextualKeyword("any")) return true; // "isolated" can be an argument label, but it's also a contextual keyword, diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 1fadcc4de2f..f7b406ef5d9 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -804,10 +804,15 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. SourceLoc opaqueLoc; + SourceLoc anyLoc; if (Tok.isContextualKeyword("some")) { // Treat some as a keyword. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); opaqueLoc = consumeToken(); + } else if (Tok.isContextualKeyword("any")) { + // Treat any as a keyword. + TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); + anyLoc = consumeToken(); } else { // This isn't a some type. SomeTypeContext.setTransparent(); @@ -816,6 +821,8 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { auto applyOpaque = [&](TypeRepr *type) -> TypeRepr* { if (opaqueLoc.isValid()) { type = new (Context) OpaqueReturnTypeRepr(opaqueLoc, type); + } else if (anyLoc.isValid()) { + type = new (Context) ExistentialTypeRepr(anyLoc, type); } return type; }; @@ -866,16 +873,21 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { consumeToken(); // consume '&' } - // Diagnose invalid `some` after an ampersand. - if (Tok.isContextualKeyword("some")) { + // Diagnose invalid `some` or `any` after an ampersand. + if (Tok.isContextualKeyword("some") || + Tok.isContextualKeyword("any")) { + auto keyword = Tok.getText(); auto badLoc = consumeToken(); - diagnose(badLoc, diag::opaque_mid_composition) + diagnose(badLoc, diag::opaque_mid_composition, keyword) .fixItRemove(badLoc) - .fixItInsert(FirstTypeLoc, "some "); + .fixItInsert(FirstTypeLoc, keyword.str() + " "); - if (opaqueLoc.isInvalid()) + if (opaqueLoc.isInvalid()) { opaqueLoc = badLoc; + } else if (anyLoc.isInvalid()) { + anyLoc = badLoc; + } } // Parse next type. @@ -1441,8 +1453,11 @@ bool Parser::canParseType() { // Accept 'inout' at for better recovery. consumeIf(tok::kw_inout); - if (Tok.isContextualKeyword("some")) + if (Tok.isContextualKeyword("some")) { consumeToken(); + } else if (Tok.isContextualKeyword("any")) { + consumeToken(); + } switch (Tok.getKind()) { case tok::kw_Self: From e6fafd473260f45353d8ba2f3c987ee6fe15e27e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 21 Nov 2021 18:27:15 -0500 Subject: [PATCH 12/16] [Test] Start to add tests for explicit existential types. --- test/type/explicit_existential.swift | 164 +++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 test/type/explicit_existential.swift diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift new file mode 100644 index 00000000000..b19a8020076 --- /dev/null +++ b/test/type/explicit_existential.swift @@ -0,0 +1,164 @@ +// RUN: %target-typecheck-verify-swift -enable-explicit-existential-types + +protocol HasSelfRequirements { + func foo(_ x: Self) + + func returnsOwnProtocol() -> any HasSelfRequirements +} +protocol Bar { + init() + + func bar() -> any Bar +} + +func useBarAsType(_ x: any Bar) {} + +protocol Pub : Bar { } + +func refinementErasure(_ p: any Pub) { + useBarAsType(p) +} + +typealias Compo = HasSelfRequirements & Bar + +struct CompoAssocType { + typealias Compo = HasSelfRequirements & Bar +} + +func useAsRequirement(_ x: T) { } +func useCompoAsRequirement(_ x: T) { } +func useCompoAliasAsRequirement(_ x: T) { } +func useNestedCompoAliasAsRequirement(_ x: T) { } + +func useAsWhereRequirement(_ x: T) where T: HasSelfRequirements {} +func useCompoAsWhereRequirement(_ x: T) where T: HasSelfRequirements & Bar {} +func useCompoAliasAsWhereRequirement(_ x: T) where T: Compo {} +func useNestedCompoAliasAsWhereRequirement(_ x: T) where T: CompoAssocType.Compo {} + +func useAsType(_: any HasSelfRequirements, + _: any HasSelfRequirements & Bar, + _: any Compo, + _: any CompoAssocType.Compo) { } + +struct TypeRequirement {} +struct CompoTypeRequirement {} +struct CompoAliasTypeRequirement {} +struct NestedCompoAliasTypeRequirement {} + +struct CompoTypeWhereRequirement where T: HasSelfRequirements & Bar {} +struct CompoAliasTypeWhereRequirement where T: Compo {} +struct NestedCompoAliasTypeWhereRequirement where T: CompoAssocType.Compo {} + +struct Struct1 { } + +typealias T1 = Pub & Bar +typealias T2 = any Pub & Bar + +protocol HasAssoc { + associatedtype Assoc + func foo() +} + +do { + enum MyError: Error { + case bad(Any) + } + + func checkIt(_ js: Any) throws { + switch js { + case let dbl as any HasAssoc: + throw MyError.bad(dbl) + + default: + fatalError("wrong") + } + } +} + +func testHasAssoc(_ x: Any, _: any HasAssoc) { + if let p = x as? any HasAssoc { + p.foo() + } + + struct ConformingType : HasAssoc { + typealias Assoc = Int + func foo() {} + + func method() -> any HasAssoc {} + } +} + +var b: any HasAssoc + +protocol P {} +typealias MoreHasAssoc = HasAssoc & P +func testHasMoreAssoc(_ x: Any) { + if let p = x as? any MoreHasAssoc { + p.foo() + } +} + +typealias X = Struct1 +_ = Struct1.self + +typealias AliasWhere = T +where T: HasAssoc, T.Assoc == any HasAssoc + +struct StructWhere +where T: HasAssoc, + T.Assoc == any HasAssoc {} + +protocol ProtocolWhere where T == any HasAssoc { + associatedtype T + + associatedtype U: HasAssoc + where U.Assoc == any HasAssoc +} + +extension HasAssoc where Assoc == any HasAssoc {} + +func FunctionWhere(_: T) +where T : HasAssoc, + T.Assoc == any HasAssoc {} + +struct SubscriptWhere { + subscript(_: T) -> Int + where T : HasAssoc, + T.Assoc == any HasAssoc { + get {} + set {} + } +} + +struct OuterGeneric { + func contextuallyGenericMethod() where T == any HasAssoc {} +} + +func testInvalidAny() { + struct S: HasAssoc { + typealias Assoc = Int + func foo() {} + } + let _: any S = S() // expected-error{{'any' has no effect on concrete type 'S'}} + + func generic(t: T) { + let _: any T = t // expected-error{{'any' has no effect on type parameter 'T'}} + let _: any T.Assoc // expected-error {{'any' has no effect on type parameter 'T.Assoc'}} + } + + let _: any ((S) -> Void) = generic // expected-error{{'any' has no effect on concrete type '(S) -> Void'}} +} + +func testRedundantAnyWarning() { + let _: any Any // expected-warning {{'any' is redundant on type 'Any'}} + let _: any AnyObject // expected-warning {{'any' is redundant on type 'AnyObject'}} +} + +protocol P1 {} +protocol P2 {} +struct ConcreteComposition: P1, P2 {} + +func testMetatypes() { + let _: any P1.Type = ConcreteComposition.self + let _: any (P1 & P2).Type = ConcreteComposition.self +} From 3cd603297c4aa477e98ead574e5ab29dd9e95a09 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 8 Dec 2021 23:40:44 -0800 Subject: [PATCH 13/16] [ASTDemangler] Reconstruct protocol composition types with ExistentialType when explicit existentials are enabled. --- lib/AST/ASTDemangler.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 31b5af311c3..adda3f0634e 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -589,7 +589,11 @@ 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 = ExistentialType::get(composition); + } + return composition; } static MetatypeRepresentation @@ -607,6 +611,8 @@ getMetatypeRepresentation(ImplMetatypeRepresentation repr) { Type ASTBuilder::createExistentialMetatypeType(Type instance, Optional repr) { + if (auto existential = instance->getAs()) + instance = existential->getConstraintType(); if (!instance->isAnyExistentialType()) return Type(); if (!repr) From 0b29ba938371c71b30cc7f54b2ec5a4fe907a81e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 9 Dec 2021 23:11:59 -0800 Subject: [PATCH 14/16] [TypeResolver] Don't use ExistentialType for Any and AnyObject for now. --- lib/AST/ASTDemangler.cpp | 3 ++- lib/Sema/TypeCheckType.cpp | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index adda3f0634e..ff46d1b8331 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -590,7 +590,8 @@ Type ASTBuilder::createProtocolCompositionType( if (superclass && superclass->getClassOrBoundGenericClass()) members.push_back(superclass); Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound); - if (Ctx.LangOpts.EnableExplicitExistentialTypes) { + if (Ctx.LangOpts.EnableExplicitExistentialTypes && + !(composition->isAny() || composition->isAnyObject())) { composition = ExistentialType::get(composition); } return composition; diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 9b329337216..a1754c2a259 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3364,8 +3364,9 @@ TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, return ErrorType::get(getASTContext()); } - bool isConstraintType = (result->is() || - result->is()); + // FIXME: Don't use ExistentialType for AnyObject for now. + bool isConstraintType = (result->is() && + !result->isAnyObject()); if (isConstraintType && getASTContext().LangOpts.EnableExplicitExistentialTypes && options.isConstraintImplicitExistential()) { @@ -3738,7 +3739,8 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, ProtocolCompositionType::get(getASTContext(), Members, /*HasExplicitAnyObject=*/false); if (getASTContext().LangOpts.EnableExplicitExistentialTypes && - options.isConstraintImplicitExistential()) { + options.isConstraintImplicitExistential() && + !composition->isAny()) { composition = ExistentialType::get(composition); } return composition; @@ -3767,6 +3769,7 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, diagnose(repr->getLoc(), diag::unnecessary_any, constraintType) .fixItRemove({anyStart, anyEnd}); + return constraintType; } return ExistentialType::get(constraintType); @@ -3954,7 +3957,7 @@ public: // Arbitrary protocol constraints are okay for 'any' types. if (isa(T)) return false; - + visit(T); return true; } From 8db129bf92be5af5a15c6cf7f31356648ecb1b01 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 9 Dec 2021 23:12:52 -0800 Subject: [PATCH 15/16] [ASTPrinter] Print 'any' for existential metatypes when explicit existential types are enabled. --- lib/AST/ASTPrinter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 9944e6c9a91..08d4b5defa5 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4739,10 +4739,15 @@ public: case MetatypeRepresentation::ObjC: Printer << "@objc_metatype "; break; } } + + auto &ctx = T->getASTContext(); + if (T->is() && + ctx.LangOpts.EnableExplicitExistentialTypes) + Printer << "any "; + printWithParensIfNotSimple(T->getInstanceType()); // We spell normal metatypes of existential types as .Protocol. - auto &ctx = T->getASTContext(); if (isa(T) && T->getInstanceType()->isAnyExistentialType() && !ctx.LangOpts.EnableExplicitExistentialTypes) { From f96104d2d5548a59835896ea95991079ec2e7340 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 10 Dec 2021 00:11:03 -0800 Subject: [PATCH 16/16] [Test] Duplicate test/TypeDecoder/existentials.swift with explicit existential types enabled. --- test/TypeDecoder/explicit_existentials.swift | 112 +++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 test/TypeDecoder/explicit_existentials.swift diff --git a/test/TypeDecoder/explicit_existentials.swift b/test/TypeDecoder/explicit_existentials.swift new file mode 100644 index 00000000000..8fd42ae67bd --- /dev/null +++ b/test/TypeDecoder/explicit_existentials.swift @@ -0,0 +1,112 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -Xfrontend -enable-explicit-existential-types -emit-executable %s -g -o %t/existentials -emit-module +// RUN: sed -ne '/\/\/ *DEMANGLE: /s/\/\/ *DEMANGLE: *//p' < %s > %t/input +// RUN: %lldb-moduleimport-test %t/existentials -type-from-mangled=%t/input | %FileCheck %s + +func blackHole(_: Any...) {} + +protocol P {} +protocol Q {} +class C {} + +class D : C, P, Q {} + +do { + let e0: Any = D() + let e1: AnyObject = D() + + let e2: P = D() + let e4: P & C = D() + let e3: P & AnyObject = D() + + let e5: P & Q = D() + let e6: P & Q & C = D() + let e7: P & Q & AnyObject = D() + + blackHole(e0, e1, e2, e3, e4, e5, e6, e7) +} + +do { + let e0: Any.Type = D.self + let e1: AnyObject.Type = D.self + + let e2: P.Type = D.self + let e4: (P & C).Type = D.self + let e3: (P & AnyObject).Type = D.self + + let e5: (P & Q).Type = D.self + let e6: (P & Q & C).Type = D.self + let e7: (P & Q & AnyObject).Type = D.self + + blackHole(e0, e1, e2, e3, e4, e5, e6, e7) +} + +do { + let e0: Any.Protocol = Any.self + let e1: AnyObject.Protocol = AnyObject.self + + let e2: P.Protocol = P.self + let e4: (P & C).Protocol = (P & C).self + let e3: (P & AnyObject).Protocol = (P & AnyObject).self + + let e5: (P & Q).Protocol = (P & Q).self + let e6: (P & Q & C).Protocol = (P & Q & C).self + let e7: (P & Q & AnyObject).Protocol = (P & Q & AnyObject).self + + blackHole(e0, e1, e2, e3, e4, e5, e6, e7) +} + +// DEMANGLE: $sypD +// DEMANGLE: $syXlD +// DEMANGLE: $s12existentials1P_pD +// DEMANGLE: $s12existentials1P_AA1CCXcD +// DEMANGLE: $s12existentials1P_XlD +// DEMANGLE: $s12existentials1P_AA1QpD +// DEMANGLE: $s12existentials1P_AA1QAA1CCXcD +// DEMANGLE: $s12existentials1P_AA1QXlD + +// CHECK: Any +// CHECK: AnyObject +// CHECK: P +// CHECK: C & P +// CHECK: P & AnyObject +// CHECK: P & Q +// CHECK: C & P & Q +// CHECK: P & Q & AnyObject + +// DEMANGLE: $sypXpD +// DEMANGLE: $syXlXpD +// DEMANGLE: $s12existentials1P_pXpD +// DEMANGLE: $s12existentials1P_XlXpD +// DEMANGLE: $s12existentials1P_AA1CCXcXpD +// DEMANGLE: $s12existentials1P_AA1QpXpD +// DEMANGLE: $s12existentials1P_AA1QAA1CCXcXpD +// DEMANGLE: $s12existentials1P_AA1QXlXpD + +// CHECK: Any.Type +// CHECK: AnyObject.Type +// CHECK: P.Type +// CHECK: (P & AnyObject).Type +// CHECK: (C & P).Type +// CHECK: (P & Q).Type +// CHECK: (C & P & Q).Type +// CHECK: (P & Q & AnyObject).Type + +// DEMANGLE: $sypmD +// DEMANGLE: $syXlmD +// DEMANGLE: $s12existentials1P_pmD +// DEMANGLE: $s12existentials1P_AA1CCXcmD +// DEMANGLE: $s12existentials1P_XlmD +// DEMANGLE: $s12existentials1P_AA1QpmD +// DEMANGLE: $s12existentials1P_AA1QAA1CCXcmD +// DEMANGLE: $s12existentials1P_AA1QXlmD + +// CHECK: Any.Protocol +// CHECK: AnyObject.Protocol +// CHECK: P.Protocol +// CHECK: (C & P).Protocol +// CHECK: (P & AnyObject).Protocol +// CHECK: (P & Q).Protocol +// CHECK: (C & P & Q).Protocol +// CHECK: (P & Q & AnyObject).Protocol