From cf4488922c2e9ede78784ac26eb45af10bee0b26 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 13 Dec 2021 19:56:33 -0800 Subject: [PATCH 01/24] [ClangImporter] When importing ObjC pointer types, wrap protocol compositions in ExistentialType when explicit existentials are enabled. --- lib/ClangImporter/ImportType.cpp | 7 +++++++ test/ClangImporter/objc_bridging_generics.swift | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 1273db09499a0..3fad5a793e358 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1052,6 +1052,9 @@ namespace { Type importedTypeArg = ProtocolCompositionType::get( Impl.SwiftContext, memberTypes, hasExplicitAnyObject); + if (Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) { + importedTypeArg = ExistentialType::get(importedTypeArg); + } importedTypeArgs.push_back(importedTypeArg); } } @@ -1203,6 +1206,10 @@ namespace { importedType = ProtocolCompositionType::get(Impl.SwiftContext, members, /*HasExplicitAnyObject=*/false); + + if (Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) { + importedType = ExistentialType::get(importedType); + } } // Class or Class

maps to an existential metatype. diff --git a/test/ClangImporter/objc_bridging_generics.swift b/test/ClangImporter/objc_bridging_generics.swift index 8de2c4b6be402..b819a82433f2e 100644 --- a/test/ClangImporter/objc_bridging_generics.swift +++ b/test/ClangImporter/objc_bridging_generics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -verify -swift-version 4 -I %S/Inputs/custom-modules %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -enable-explicit-existential-types -verify -swift-version 4 -I %S/Inputs/custom-modules %s // REQUIRES: objc_interop From f368b77ad5d78f20004558efc775375f3ba5623f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 13 Dec 2021 20:56:10 -0800 Subject: [PATCH 02/24] [Sema] Account for ExistentialType in lookupExistentialConformance. --- lib/AST/Module.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 07274fee10894..c406e45c9f242 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -978,13 +978,17 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { // existential to an archetype parameter, so for now we restrict this to // @objc protocols and marker protocols. if (!layout.isObjC() && !protocol->isMarkerProtocol()) { + auto constraint = type; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + // There's a specific exception for protocols with self-conforming // witness tables, but the existential has to be *exactly* that type. // TODO: synthesize witness tables on-demand for protocol compositions // that can satisfy the requirement. if (protocol->requiresSelfConformanceWitnessTable() && - type->is() && - type->castTo()->getDecl() == protocol) + constraint->is() && + constraint->castTo()->getDecl() == protocol) return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); return ProtocolConformanceRef::forInvalid(); From 02f5e4738653f52600325f76851c997d4b16c265 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 13 Dec 2021 21:28:02 -0800 Subject: [PATCH 03/24] [Sema] Resolve @_implements protocols using the generic requirement type resolver context. --- lib/Sema/TypeCheckAttr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 77ef0a640a3dd..e310765773040 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3000,8 +3000,10 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { Type T = attr->getProtocolType(); if (!T && attr->getProtocolTypeRepr()) { + auto context = TypeResolverContext::GenericRequirement; T = TypeResolution::resolveContextualType(attr->getProtocolTypeRepr(), DC, - None, /*unboundTyOpener*/ nullptr, + TypeResolutionOptions(context), + /*unboundTyOpener*/ nullptr, /*placeholderHandler*/ nullptr); } From efeb709f6586af1286e176ab791b720084d1d9dc Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 10:33:58 -0500 Subject: [PATCH 04/24] [Deserialization] Fix deserialization of explicit existential types. --- lib/Serialization/Deserialization.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4e6678f7c7821..96593dd81be82 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6117,6 +6117,7 @@ Expected TypeDeserializer::getTypeCheckedImpl() { CASE(SequenceArchetype) CASE(GenericTypeParam) CASE(ProtocolComposition) + CASE(Existential) CASE(DependentMember) CASE(BoundGeneric) CASE(SILBlockStorage) From 6cee193fc05628f437b512404a3a1cb6c362a487 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 11:52:50 -0500 Subject: [PATCH 05/24] [Type System] When explicit existential types are enabled, wrap Error in ExistentialType for the type of error values. --- include/swift/AST/ASTContext.h | 1 + include/swift/AST/ASTSynthesis.h | 2 +- lib/AST/ASTContext.cpp | 14 ++++++++++++-- lib/AST/ASTVerifier.cpp | 9 +++++---- lib/IDE/Refactoring.cpp | 3 ++- lib/IRGen/GenBuiltin.cpp | 5 ++--- lib/SIL/IR/SILType.cpp | 2 +- lib/SIL/Utils/BasicBlockUtils.cpp | 4 ++-- lib/SIL/Verifier/SILVerifier.cpp | 2 +- lib/SILGen/ResultPlan.cpp | 9 ++++----- lib/SILGen/SILGen.cpp | 5 +++-- lib/SILGen/SILGenBridging.cpp | 2 +- lib/SILGen/SILGenBuiltin.cpp | 3 +-- lib/SILGen/SILGenExpr.cpp | 3 +-- lib/SILGen/SILGenForeignError.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 6 +++--- lib/Sema/CSClosure.cpp | 7 +++---- lib/Sema/ConstraintSystem.cpp | 8 +++++--- lib/Sema/TypeCheckDeclObjC.cpp | 3 ++- lib/Sema/TypeCheckStmt.cpp | 9 +++++---- 20 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index f57f390143a12..d98502304b0c0 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -532,6 +532,7 @@ class ASTContext final { /// Retrieve the declaration of Swift.Error. ProtocolDecl *getErrorDecl() const; CanType getExceptionType() const; + CanType getErrorExistentialType() const; #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** Retrieve the declaration of Swift.NAME. */ \ diff --git a/include/swift/AST/ASTSynthesis.h b/include/swift/AST/ASTSynthesis.h index 7ea55cf8c028f..6437e6a1cee5b 100644 --- a/include/swift/AST/ASTSynthesis.h +++ b/include/swift/AST/ASTSynthesis.h @@ -56,7 +56,7 @@ inline Type synthesizeType(SynthesisContext &SC, switch (kind) { case _any: return SC.Context.TheAnyType; case _bridgeObject: return SC.Context.TheBridgeObjectType; - case _error: return SC.Context.getExceptionType(); + case _error: return SC.Context.getErrorExistentialType(); case _executor: return SC.Context.TheExecutorType; case _job: return SC.Context.TheJobType; case _nativeObject: return SC.Context.TheNativeObjectType; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 21bfef2986770..9ff1e3804b0f0 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -860,6 +860,16 @@ ProtocolDecl *ASTContext::getErrorDecl() const { return getProtocol(KnownProtocolKind::Error); } +CanType ASTContext::getErrorExistentialType() const { + Type errorType = getExceptionType(); + if (LangOpts.EnableExplicitExistentialTypes && + errorType->isConstraintType()) { + errorType = ExistentialType::get(errorType); + } + + return errorType->getCanonicalType(); +} + EnumElementDecl *ASTContext::getOptionalSomeDecl() const { if (!getImpl().OptionalSomeDecl) getImpl().OptionalSomeDecl = getOptionalDecl()->getUniqueElement(/*hasVal*/true); @@ -4865,7 +4875,7 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, if (auto nsErrorTy = getNSErrorType()) { // The corresponding value type is Error. if (bridgedValueType) - *bridgedValueType = getErrorDecl()->getDeclaredInterfaceType(); + *bridgedValueType = getErrorExistentialType(); return nsErrorTy; } @@ -4902,7 +4912,7 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, if (findConformance(KnownProtocolKind::Error)) { // The corresponding value type is Error. if (bridgedValueType) - *bridgedValueType = getErrorDecl()->getDeclaredInterfaceType(); + *bridgedValueType = getErrorExistentialType(); // Bridge to NSError. if (auto nsErrorTy = getNSErrorType()) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index b4f4c6931ac52..f3d88ba6be144 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -3422,11 +3422,12 @@ class Verifier : public ASTWalker { } Type checkExceptionTypeExists(const char *where) { - auto exn = Ctx.getErrorDecl(); - if (exn) return exn->getDeclaredInterfaceType(); + if (!Ctx.getErrorDecl()) { + Out << "exception type does not exist in " << where << "\n"; + abort(); + } - Out << "exception type does not exist in " << where << "\n"; - abort(); + return Ctx.getErrorExistentialType(); } bool isGoodSourceRange(SourceRange SR) { diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 917a2474c0f35..8035c45f87886 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -7831,7 +7831,8 @@ class AsyncConverter : private SourceEntityWalker { const AsyncHandlerDesc &HandlerDesc) { // If the error type is already Error, we can pass it as-is. auto ErrorType = *HandlerDesc.getErrorType(); - if (ErrorType->getCanonicalType() == getASTContext().getExceptionType()) { + if (ErrorType->getCanonicalType() == + getASTContext().getErrorExistentialType()) { OS << ErrorName; return; } diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index e6d483391bbed..60e7f20738266 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -592,10 +592,9 @@ if (Builtin.ID == BuiltinValueKind::id) { \ auto *fn = cast(IGF.IGM.getWillThrowFn()); auto error = args.claimNext(); + auto errorTy = IGF.IGM.Context.getErrorExistentialType(); auto errorBuffer = IGF.getCalleeErrorResultSlot( - SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() - ->getDeclaredInterfaceType() - ->getCanonicalType())); + SILType::getPrimitiveObjectType(errorTy)); IGF.Builder.CreateStore(error, errorBuffer); auto context = llvm::UndefValue::get(IGF.IGM.Int8PtrTy); diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 046e68782487d..d9faf0009361d 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -45,7 +45,7 @@ CanArchetypeType swift::getOpenedArchetypeOf(CanType Ty) { } SILType SILType::getExceptionType(const ASTContext &C) { - return SILType::getPrimitiveObjectType(C.getExceptionType()); + return SILType::getPrimitiveObjectType(C.getErrorExistentialType()); } SILType SILType::getNativeObjectType(const ASTContext &C) { diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index 4dcdc2800d1a4..3a1f46bdd532f 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -144,8 +144,8 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, case 1: { assert(AACI->getErrorBB()); auto &C = AACI->getFunction()->getASTContext(); - auto errorTy = C.getErrorDecl()->getDeclaredType(); - auto errorSILTy = SILType::getPrimitiveObjectType(errorTy->getCanonicalType()); + auto errorTy = C.getErrorExistentialType(); + auto errorSILTy = SILType::getPrimitiveObjectType(errorTy); // error BB. this takes the error value argument args.push_back( newEdgeBB->createPhiArgument(errorSILTy, OwnershipKind::Owned)); diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 3bfaee4e0a897..fe33017a83692 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -5252,7 +5252,7 @@ class SILVerifier : public SILVerifierBase { require(AACI->getErrorBB()->getNumArguments() == 1, "error successor must take one argument"); auto arg = AACI->getErrorBB()->getArgument(0); - auto errorType = C.getErrorDecl()->getDeclaredType()->getCanonicalType(); + auto errorType = C.getErrorExistentialType(); requireSameType(arg->getType(), SILType::getPrimitiveObjectType(errorType), "error successor argument must have Error type"); diff --git a/lib/SILGen/ResultPlan.cpp b/lib/SILGen/ResultPlan.cpp index 8728c07973570..f229d5d497b40 100644 --- a/lib/SILGen/ResultPlan.cpp +++ b/lib/SILGen/ResultPlan.cpp @@ -508,7 +508,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { auto continuationDecl = SGF.getASTContext().getUnsafeContinuationDecl(); auto errorTy = throws - ? SGF.getASTContext().getExceptionType() + ? SGF.getASTContext().getErrorExistentialType() : SGF.getASTContext().getNeverType(); auto continuationTy = BoundGenericType::get(continuationDecl, Type(), { calleeTypeInfo.substResultType, errorTy }) @@ -618,7 +618,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { auto continuationDecl = SGF.getASTContext().getUnsafeContinuationDecl(); - auto errorTy = SGF.getASTContext().getExceptionType(); + auto errorTy = SGF.getASTContext().getErrorExistentialType(); auto continuationBGT = BoundGenericType::get(continuationDecl, Type(), {calleeTypeInfo.substResultType, errorTy}); @@ -666,9 +666,8 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { SGF.B.emitBlock(errorBlock); Scope errorScope(SGF, loc); - - auto errorTy = SGF.getASTContext().getErrorDecl()->getDeclaredType() - ->getCanonicalType(); + + auto errorTy = SGF.getASTContext().getErrorExistentialType(); auto errorVal = SGF.B.createTermResult( SILType::getPrimitiveObjectType(errorTy), OwnershipKind::Owned); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index f3febf7c8ed8f..bd85b869b83b9 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -168,6 +168,7 @@ getBridgingFn(Optional &cacheSlot, #define REQUIRED(X) Types.get##X##Type() #define OPTIONAL(X) OptionalType::get(Types.get##X##Type()) +#define EXISTENTIAL(X) getASTContext().get##X##ExistentialType() #define GET_BRIDGING_FN(Module, FromKind, FromTy, ToKind, ToTy) \ SILDeclRef SILGenModule::get##FromTy##To##ToTy##Fn() { \ @@ -182,8 +183,8 @@ GET_BRIDGING_FN(Darwin, REQUIRED, Bool, REQUIRED, DarwinBoolean) GET_BRIDGING_FN(Darwin, REQUIRED, DarwinBoolean, REQUIRED, Bool) GET_BRIDGING_FN(ObjectiveC, REQUIRED, Bool, REQUIRED, ObjCBool) GET_BRIDGING_FN(ObjectiveC, REQUIRED, ObjCBool, REQUIRED, Bool) -GET_BRIDGING_FN(Foundation, OPTIONAL, NSError, REQUIRED, Error) -GET_BRIDGING_FN(Foundation, REQUIRED, Error, REQUIRED, NSError) +GET_BRIDGING_FN(Foundation, OPTIONAL, NSError, EXISTENTIAL, Error) +GET_BRIDGING_FN(Foundation, EXISTENTIAL, Error, REQUIRED, NSError) GET_BRIDGING_FN(WinSDK, REQUIRED, Bool, REQUIRED, WindowsBool) GET_BRIDGING_FN(WinSDK, REQUIRED, WindowsBool, REQUIRED, Bool) diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 1d9b726e7ec3c..c70dd365f471d 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -1252,7 +1252,7 @@ ManagedValue SILGenFunction::emitNativeToBridgedError(SILLocation loc, // FIXME: maybe we should use a different entrypoint for this case, to // avoid the code size and performance overhead of forming the box? nativeError = emitUnabstractedCast(*this, loc, nativeError, nativeType, - getASTContext().getExceptionType()); + getASTContext().getErrorExistentialType()); auto bridgeFn = emitGlobalFunctionRef(loc, SGM.getErrorToNSErrorFn()); auto bridgeFnType = bridgeFn->getType().castTo(); diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index efa71d2a76708..10a75bbcf3b67 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1564,8 +1564,7 @@ static ManagedValue emitBuiltinWithUnsafeContinuation( Scope errorScope(SGF, loc); - auto errorTy = SGF.getASTContext().getErrorDecl()->getDeclaredType() - ->getCanonicalType(); + auto errorTy = SGF.getASTContext().getErrorExistentialType(); auto errorVal = SGF.B.createTermResult( SILType::getPrimitiveObjectType(errorTy), OwnershipKind::Owned); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 45d9840d9dd43..15f720d0c5746 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1037,8 +1037,7 @@ SILValue SILGenFunction::emitTemporaryAllocation(SILLocation loc, SILType ty, if (auto *DRE = loc.getAsASTNode()) if (auto *VD = dyn_cast(DRE->getDecl())) if (!isa(VD) && VD->isImplicit() && - (VD->getType()->is() || - VD->getType()->is()) && + VD->getType()->isExistentialType() && VD->getType()->getExistentialLayout().isErrorExistential()) { DbgVar = SILDebugVariable(VD->isLet(), 0); loc = SILLocation(VD); diff --git a/lib/SILGen/SILGenForeignError.cpp b/lib/SILGen/SILGenForeignError.cpp index 4c95fc44deaa7..30edca104fa74 100644 --- a/lib/SILGen/SILGenForeignError.cpp +++ b/lib/SILGen/SILGenForeignError.cpp @@ -134,7 +134,7 @@ namespace { SILValue emitBridged(SILGenFunction &SGF, SILLocation loc, CanType bridgedErrorProto) const override { auto nativeErrorType = NativeError->getType().getASTType(); - assert(nativeErrorType == SGF.SGM.getASTContext().getExceptionType()); + assert(nativeErrorType == SGF.SGM.getASTContext().getErrorExistentialType()); SILValue bridgedError = SGF.emitNativeToBridgedError(loc, SGF.emitManagedRValueWithCleanup(NativeError), diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index cbeac56f61175..de244e631fbe9 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -914,14 +914,14 @@ class BuilderClosureVisitor /// Visit a throw statement, which never produces a result. VarDecl *visitThrowStmt(ThrowStmt *throwStmt) { - Type exnType = ctx.getErrorDecl()->getDeclaredInterfaceType(); - if (!exnType) { + if (!ctx.getErrorDecl()) { hadError = true; } if (cs) { SolutionApplicationTarget target( - throwStmt->getSubExpr(), dc, CTP_ThrowStmt, exnType, + throwStmt->getSubExpr(), dc, CTP_ThrowStmt, + ctx.getErrorExistentialType(), /*isDiscarded=*/false); if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow)) hadError = true; diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index c665235bece3e..6bcd97622ebc4 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -621,13 +621,12 @@ class ClosureConstraintGenerator assert(isSupportedMultiStatementClosure() && "Unsupported statement: Throw"); - Type errType = - cs.getASTContext().getErrorDecl()->getDeclaredInterfaceType(); - if (!errType) { + if (!cs.getASTContext().getErrorDecl()) { hadError = true; return; } + auto errType = cs.getASTContext().getErrorExistentialType(); auto *errorExpr = throwStmt->getSubExpr(); createConjunction( @@ -739,7 +738,7 @@ class ClosureConstraintGenerator auto *switchStmt = cast(parent.get()); contextualTy = cs.getType(switchStmt->getSubjectExpr()); } else if (parent.isStmt(StmtKind::DoCatch)) { - contextualTy = cs.getASTContext().getExceptionType(); + contextualTy = cs.getASTContext().getErrorExistentialType(); } else { hadError = true; return; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 258738a70b0b9..5544a8d065a23 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2506,14 +2506,16 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate( // Okay, now it should be safe to coerce the pattern. // Pull the top-level pattern back out. pattern = LabelItem.getPattern(); - Type exnType = DC->getASTContext().getErrorDecl()->getDeclaredInterfaceType(); - if (!exnType) + auto &ctx = DC->getASTContext(); + if (!ctx.getErrorDecl()) return false; + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); pattern = TypeChecker::coercePatternToType( - contextualPattern, exnType, TypeResolverContext::InExpression); + contextualPattern, ctx.getErrorExistentialType(), + TypeResolverContext::InExpression); if (!pattern) return false; diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index e6b7906a80a04..817d30eac58f6 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -806,7 +806,8 @@ bool swift::isRepresentableInObjC( Optional completionHandlerErrorParamIndex; if (FD->hasThrows()) { completionHandlerErrorParamIndex = completionHandlerParams.size(); - addCompletionHandlerParam(OptionalType::get(ctx.getExceptionType())); + auto errorType = ctx.getErrorExistentialType(); + addCompletionHandlerParam(OptionalType::get(errorType)); } Type completionHandlerType = FunctionType::get( diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 3b7ba396b179e..533eec0efd238 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -855,10 +855,11 @@ class StmtChecker : public StmtVisitor { // Coerce the operand to the exception type. auto E = TS->getSubExpr(); - Type exnType = getASTContext().getErrorDecl()->getDeclaredInterfaceType(); - if (!exnType) return TS; + if (!getASTContext().getErrorDecl()) + return TS; - TypeChecker::typeCheckExpression(E, DC, {exnType, CTP_ThrowStmt}); + Type errorType = getASTContext().getErrorExistentialType(); + TypeChecker::typeCheckExpression(E, DC, {errorType, CTP_ThrowStmt}); TS->setSubExpr(E); return TS; @@ -1205,7 +1206,7 @@ class StmtChecker : public StmtVisitor { auto catches = S->getCatches(); checkSiblingCaseStmts(catches.begin(), catches.end(), CaseParentKind::DoCatch, limitExhaustivityChecks, - getASTContext().getExceptionType()); + getASTContext().getErrorExistentialType()); return S; } From a7fa4695b2f504e1e3840e7833d51d7bad2120c3 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:02:26 -0500 Subject: [PATCH 06/24] [GenericSignature] Don't allow conformance requirements with explicit existential types. --- lib/AST/ASTContext.cpp | 12 +++++++----- lib/AST/GenericSignatureBuilder.cpp | 3 ++- lib/SILGen/SILGenPoly.cpp | 7 ++++++- lib/SILOptimizer/Differentiation/Thunk.cpp | 7 ++++++- .../ExistentialTransform.cpp | 7 ++++++- test/type/explicit_existential.swift | 2 ++ 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 9ff1e3804b0f0..f528265ca8910 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -5000,16 +5000,18 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { // constraints while existential values do. CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) { assert(type->isExistentialType()); + if (auto existential = type->getAs()) + type = existential->getConstraintType(); - const CanType existential = type->getCanonicalType(); + const CanType constraint = type->getCanonicalType(); // The opened archetype signature for a protocol type is identical // to the protocol's own canonical generic signature. - if (const auto protoTy = dyn_cast(existential)) { + if (const auto protoTy = dyn_cast(constraint)) { return protoTy->getDecl()->getGenericSignature().getCanonicalSignature(); } - auto found = getImpl().ExistentialSignatures.find(existential); + auto found = getImpl().ExistentialSignatures.find(constraint); if (found != getImpl().ExistentialSignatures.end()) return found->second; @@ -5017,7 +5019,7 @@ CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) { GenericTypeParamType::get(/*type sequence*/ false, /*depth*/ 0, /*index*/ 0, *this); Requirement requirement(RequirementKind::Conformance, genericParam, - existential); + constraint); auto genericSig = buildGenericSignature(*this, GenericSignature(), {genericParam}, @@ -5026,7 +5028,7 @@ CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) { CanGenericSignature canGenericSig(genericSig); auto result = getImpl().ExistentialSignatures.insert( - std::make_pair(existential, canGenericSig)); + std::make_pair(constraint, canGenericSig)); assert(result.second); (void) result; diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index f223f310a0e61..380e0f4dd27a8 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -4500,7 +4500,8 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( } // Check whether we have a reasonable constraint type at all. - if (!constraintType->isExistentialType() && + if (!constraintType->is() && + !constraintType->is() && !constraintType->getClassOrBoundGenericClass()) { if (source.getLoc().isValid() && !constraintType->hasError()) { Impl->HadAnyError = true; diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index a044e6c57447f..c25e26806628c 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3052,8 +3052,13 @@ buildThunkSignature(SILGenFunction &SGF, // Add a new generic parameter to replace the opened existential. auto *newGenericParam = GenericTypeParamType::get(/*type sequence*/ false, depth, 0, ctx); + + auto constraint = openedExistential->getOpenedExistentialType(); + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + Requirement newRequirement(RequirementKind::Conformance, newGenericParam, - openedExistential->getOpenedExistentialType()); + constraint); auto genericSig = buildGenericSignature(ctx, baseGenericSig, { newGenericParam }, diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index e9f5a88cafc7d..2f394f4335fa5 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -66,8 +66,13 @@ CanGenericSignature buildThunkSignature(SILFunction *fn, bool inheritGenericSig, // Add a new generic parameter to replace the opened existential. auto *newGenericParam = GenericTypeParamType::get(/*type sequence*/ false, depth, 0, ctx); + + auto constraint = openedExistential->getOpenedExistentialType(); + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + Requirement newRequirement(RequirementKind::Conformance, newGenericParam, - openedExistential->getOpenedExistentialType()); + constraint); auto genericSig = buildGenericSignature(ctx, baseGenericSig, { newGenericParam }, diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 47173c70b4aac..5b68e78386ad8 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -304,12 +304,17 @@ void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes( auto ¶m = params[Idx]; auto PType = param.getArgumentType(M, FTy, F->getTypeExpansionContext()); assert(PType.isExistentialType()); + + CanType constraint = PType; + if (auto existential = PType->getAs()) + constraint = existential->getConstraintType()->getCanonicalType(); + /// Generate new generic parameter. auto *NewGenericParam = GenericTypeParamType::get(/*type sequence*/ false, Depth, GPIdx++, Ctx); genericParams.push_back(NewGenericParam); Requirement NewRequirement(RequirementKind::Conformance, NewGenericParam, - PType); + constraint); requirements.push_back(NewRequirement); ArgToGenericTypeMap.insert( std::pair(Idx, NewGenericParam)); diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index b19a802007686..ab4da2b6e2ec3 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -162,3 +162,5 @@ func testMetatypes() { let _: any P1.Type = ConcreteComposition.self let _: any (P1 & P2).Type = ConcreteComposition.self } + +func generic(_ t: T) {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'any P1'}} From f6f53f6e86670b282ddf7a61dd284570f7a70365 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:04:43 -0500 Subject: [PATCH 07/24] [Codable] When explicit existential types are enabled, synthesize Codable initializer requirements with explicit existential Encodable and Decodable parameters. --- lib/Sema/DerivedConformanceCodable.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index a3b9730280f7b..5f37ed3d119da 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -1212,6 +1212,9 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { // Create from the inside out: auto encoderType = C.getEncoderType(); + if (C.LangOpts.EnableExplicitExistentialTypes) + encoderType = ExistentialType::get(encoderType); + auto returnType = TupleType::getEmpty(C); // Params: (Encoder) @@ -1803,6 +1806,9 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { // Params: (Decoder) auto decoderType = C.getDecoderType(); + if (C.LangOpts.EnableExplicitExistentialTypes) + decoderType = ExistentialType::get(decoderType); + auto *decoderParamDecl = new (C) ParamDecl( SourceLoc(), SourceLoc(), C.Id_from, SourceLoc(), C.Id_decoder, conformanceDC); From 86730caf1839a2831a41838aa4c7d69519b2fa56 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:35:06 -0500 Subject: [PATCH 08/24] [Interop] Handle existential types in ClangTypeConverter and the ClangImporter. --- lib/AST/ClangTypeConverter.cpp | 8 ++++++++ lib/AST/ClangTypeConverter.h | 1 + lib/ClangImporter/ImportDecl.cpp | 3 +++ lib/ClangImporter/ImportType.cpp | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 7a1a4eca5b6f4..33c9ff575c232 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -750,6 +750,11 @@ ClangTypeConverter::visitProtocolCompositionType(ProtocolCompositionType *type) return clangCtx.getObjCObjectPointerType(clangType); } +clang::QualType +ClangTypeConverter::visitExistentialType(ExistentialType *type) { + return visit(type->getConstraintType()); +} + clang::QualType ClangTypeConverter::visitBuiltinRawPointerType(BuiltinRawPointerType *type) { return ClangASTContext.VoidPtrTy; @@ -828,6 +833,9 @@ clang::QualType ClangTypeConverter::convert(Type type) { return it->second; // Try to do this without making cache entries for obvious cases. + if (auto existential = type->getAs()) + type = existential->getConstraintType(); + if (auto nominal = type->getAs()) { auto decl = nominal->getDecl(); if (auto clangDecl = decl->getClangDecl()) { diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index d14a930fe6e13..d4ba69e33cf8c 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -122,6 +122,7 @@ class ClangTypeConverter : clang::QualType visitEnumType(EnumType *type); clang::QualType visitFunctionType(FunctionType *type); clang::QualType visitProtocolCompositionType(ProtocolCompositionType *type); + clang::QualType visitExistentialType(ExistentialType *type); clang::QualType visitBuiltinRawPointerType(BuiltinRawPointerType *type); clang::QualType visitBuiltinIntegerType(BuiltinIntegerType *type); clang::QualType visitBuiltinFloatType(BuiltinFloatType *type); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 91d1356458d76..59f8e1317939a 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -6244,6 +6244,9 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl, // Local function to add a known protocol only when the // underlying type conforms to it. auto computedNominal = computedPropertyUnderlyingType->getAnyNominal(); + if (auto existential = + computedPropertyUnderlyingType->getAs()) + computedNominal = existential->getConstraintType()->getAnyNominal(); auto transferKnown = [&](KnownProtocolKind kind) { if (!computedNominal) return false; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 3fad5a793e358..b83f229a4823d 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1183,6 +1183,10 @@ namespace { } } + if (bridgedType->isConstraintType() && + Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) + bridgedType = ExistentialType::get(bridgedType); + return { importedType, ImportHint(ImportHint::ObjCBridged, bridgedType) }; } @@ -2505,6 +2509,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( if (kind == SpecialMethodKind::NSDictionarySubscriptGetter && paramTy->isObjCIdType()) { swiftParamTy = SwiftContext.getNSCopyingType(); + if (SwiftContext.LangOpts.EnableExplicitExistentialTypes) + swiftParamTy = ExistentialType::get(swiftParamTy); if (!swiftParamTy) return {Type(), false}; if (optionalityOfParam != OTK_None) From 1d56338ed664390ee31b22e71eedb1c80d2fd916 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:38:23 -0500 Subject: [PATCH 09/24] [SILOptimizer] Use the constraint type in ExistentialSpecializer when finding the protocol decl for an existential type. --- .../ExistentialSpecializer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp index b1decdc3830da..57aab7934a478 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -95,12 +95,16 @@ bool ExistentialSpecializer::findConcreteTypeFromSoleConformingType( auto ArgType = Arg->getType(); auto SwiftArgType = ArgType.getASTType(); + CanType constraint = SwiftArgType; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType()->getCanonicalType(); + /// Do not handle composition types yet. - if (isa(SwiftArgType)) + if (isa(constraint)) return false; assert(ArgType.isExistentialType()); /// Find the protocol decl. - auto *PD = dyn_cast(SwiftArgType->getAnyNominal()); + auto *PD = dyn_cast(constraint->getAnyNominal()); if (!PD) return false; From f7a82ac1749f6d1635be02357bd31af8b351cac2 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:40:08 -0500 Subject: [PATCH 10/24] [Type Reconstruction] For now, reconstruct existential types without explicit ExistentialType. The ASTDemangler needs to produce ExistentialType based on context, not unconditionally when producing a protocol composition. This is tricky because existential types are mangled the same way as other protocol references, e.g. in conformance requirements. For now, drop the explicit existentials when reconstructing types. --- lib/AST/ASTDemangler.cpp | 12 ++++++++---- lib/IRGen/IRGenDebugInfo.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 698da2641b787..6e2266b7938ad 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -584,11 +584,15 @@ Type ASTBuilder::createProtocolCompositionType( members.push_back(protocol->getDeclaredInterfaceType()); if (superclass && superclass->getClassOrBoundGenericClass()) members.push_back(superclass); + + // FIXME: When explicit existential types are enabled, protocol + // compositions should be wrapped in ExistentialType based on + // context, similar to how protocol compositions are resolved + // during type resolution. For example, protocol compositions + // in parameter types should be wrapped in ExistentialType, but + // protocol compositions on the right side of a conformance + // requirement should not. Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound); - if (Ctx.LangOpts.EnableExplicitExistentialTypes && - !(composition->isAny() || composition->isAnyObject())) { - composition = ExistentialType::get(composition); - } return composition; } diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 070fafebb5316..b0fc35be773e2 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -900,6 +900,14 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { Ty->dump(llvm::errs()); abort(); } else if (!Reconstructed->isEqual(Ty) && + // FIXME: Existential types are reconstructed without + // an explicit ExistentialType wrapping the constraint. + !(Ty->getASTContext().LangOpts.EnableExplicitExistentialTypes && + Ty.transform([](Type type) -> Type { + if (auto existential = type->getAs()) + return existential->getConstraintType(); + return type; + })->isEqual(Reconstructed)) && !EqualUpToClangTypes().check(Reconstructed, Ty)) { // [FIXME: Include-Clang-type-in-mangling] Remove second check llvm::errs() << "Incorrect reconstructed type for " << Result << "\n"; From 5e4fbc48ed8ebfe9212a5d24ba9b84cd8b150901 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Dec 2021 12:47:00 -0500 Subject: [PATCH 11/24] [Diagnostics] Stage in the new error message for existential types that must be marked with 'any' as a warning. --- lib/Sema/TypeCheckType.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 2e847303218f5..dbc20c0f9fada 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -4085,7 +4085,8 @@ class ExistentialTypeVisitor if (proto->existentialRequiresAny()) { Ctx.Diags.diagnose(comp->getNameLoc(), diag::existential_requires_any, - proto->getName()); + proto->getName()) + .limitBehavior(DiagnosticBehavior::Warning); } } else if (auto *alias = dyn_cast_or_null(comp->getBoundDecl())) { auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType()); @@ -4101,7 +4102,8 @@ class ExistentialTypeVisitor Ctx.Diags.diagnose(comp->getNameLoc(), diag::existential_requires_any, - protoDecl->getName()); + protoDecl->getName()) + .limitBehavior(DiagnosticBehavior::Warning); } } return false; From 733648b4a61eb445cd220bfc5cc8f502a229f906 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 22 Dec 2021 10:48:24 -0500 Subject: [PATCH 12/24] [Sema] When explicit existential types are enabled, type witnesses that resolve to ProtocolType or ProtocolCompositionType are always existential types. This can happen because type aliases to protocol constraints can also be used as type witnesses. The type alias itself is still a constraint, because it can be used in generic signatures in addition to being used as an existential type, e.g. in parameter position. So, we need to wrap the type alias in an ExistentialType during type witness resolution. --- include/swift/AST/Type.h | 5 +++++ include/swift/AST/Types.h | 12 ++++++++++++ lib/Sema/TypeCheckProtocol.cpp | 17 +++++++++++++++++ test/type/explicit_existential.swift | 21 +++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 467753c4df00d..3b7650a430338 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -385,6 +385,7 @@ class CanType : public Type { static bool isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig, bool functionsCount); + static bool isConstraintTypeImpl(CanType type); static bool isExistentialTypeImpl(CanType type); static bool isAnyExistentialTypeImpl(CanType type); static bool isObjCExistentialTypeImpl(CanType type); @@ -457,6 +458,10 @@ class CanType : public Type { /*functions count*/ false); } + bool isConstraintType() const { + return isConstraintTypeImpl(*this); + } + /// Is this type existential? bool isExistentialType() const { return isExistentialTypeImpl(*this); diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c1fa303a8f44f..5a0573d157e4b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -727,6 +727,9 @@ class alignas(1 << TypeAlignInBits) TypeBase return getRecursiveProperties().hasDependentMember(); } + /// Whether this type represents a generic constraint. + bool isConstraintType() const; + /// isExistentialType - Determines whether this type is an existential type, /// whose real (runtime) type is unknown but which is known to conform to /// some set of protocols. Protocol and protocol-conformance types are @@ -6335,6 +6338,15 @@ inline GenericTypeParamType *TypeBase::getRootGenericParam() { return t->castTo(); } +inline bool TypeBase::isConstraintType() const { + return getCanonicalType().isConstraintType(); +} + +inline bool CanType::isConstraintTypeImpl(CanType type) { + return (isa(type) || + isa(type)); +} + inline bool TypeBase::isExistentialType() { return getCanonicalType().isExistentialType(); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 86cc1c175c58b..3414728c9a997 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4783,6 +4783,23 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( auto memberType = TypeChecker::substMemberTypeWithBase(DC->getParentModule(), typeDecl, Adoptee); + // Type witnesses that resolve to constraint types are always + // existential types. This can only happen when the type witness + // is explicitly written with a type alias. The type alias itself + // is still a constraint type because it can be used as both a + // type witness and as a generic constraint. + // + // With SE-0335, using a type alias as both a type witness and a generic + // constraint will be disallowed in Swift 6, because existential types + // must be explicit, and a generic constraint isn't a valid type witness. + // + // Note that Any and AnyObject aren't yet resolved using ExistentialType. + if (getASTContext().LangOpts.EnableExplicitExistentialTypes && + memberType->isConstraintType() && + !(memberType->isAny() || memberType->isAnyObject())) { + memberType = ExistentialType::get(memberType); + } + if (!viableTypes.insert(memberType->getCanonicalType()).second) continue; diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index ab4da2b6e2ec3..af27c20f4890d 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -164,3 +164,24 @@ func testMetatypes() { } func generic(_ t: T) {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'any P1'}} + +protocol RawRepresentable { + associatedtype RawValue + var rawValue: RawValue { get } +} + +enum E1: RawRepresentable { + typealias RawValue = P1 + + var rawValue: P1 { + return ConcreteComposition() + } +} + +enum E2: RawRepresentable { + typealias RawValue = any P1 + + var rawValue: any P1 { + return ConcreteComposition() + } +} From d971d489d63d2962cc550c7dd0c89d9f2a06b6a4 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 01:06:32 -0800 Subject: [PATCH 13/24] [Type System] With explicit existential types, make sure existential metatypes always wrap the constraint type directly, and the instance type of an existential metatype is an existential type. --- include/swift/AST/Types.h | 2 ++ include/swift/SIL/SILCloner.h | 2 +- include/swift/SIL/SILInstruction.h | 2 +- lib/AST/ASTContext.cpp | 19 ++++++++++++++++++- lib/AST/ASTVerifier.cpp | 5 ++++- lib/AST/Type.cpp | 5 +++++ lib/SIL/Verifier/SILVerifier.cpp | 11 +++++++++++ lib/SILGen/SILGenPoly.cpp | 2 +- lib/Sema/CSApply.cpp | 12 ++++++------ lib/Sema/ConstraintSystem.cpp | 8 ++++++-- test/Constraints/protocols.swift | 1 + test/SILGen/protocol_with_superclass.swift | 3 +++ test/SILGen/subclass_existentials.swift | 3 +++ 13 files changed, 62 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 5a0573d157e4b..32b0bd51fac1d 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2734,6 +2734,8 @@ class ExistentialMetatypeType : public AnyMetatypeType { static bool classof(const TypeBase *T) { return T->getKind() == TypeKind::ExistentialMetatype; } + + Type getExistentialInstanceType(); private: ExistentialMetatypeType(Type T, const ASTContext *C, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 9516c9fc1b76a..29c463576dfd5 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2220,7 +2220,7 @@ visitOpenExistentialMetatypeInst(OpenExistentialMetatypeInst *Inst) { auto openedType = Inst->getType().getASTType(); auto exType = Inst->getOperand()->getType().getASTType(); while (auto exMetatype = dyn_cast(exType)) { - exType = exMetatype.getInstanceType(); + exType = exMetatype->getExistentialInstanceType()->getCanonicalType(); openedType = cast(openedType).getInstanceType(); } remapOpenedType(cast(openedType)); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index d958ceff2ee6b..6e000ffed7904 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7072,7 +7072,7 @@ class InitExistentialMetatypeInst final auto exType = getType().getASTType(); auto concreteType = getOperand()->getType().getASTType(); while (auto exMetatype = dyn_cast(exType)) { - exType = exMetatype.getInstanceType(); + exType = exMetatype->getExistentialInstanceType()->getCanonicalType(); concreteType = cast(concreteType).getInstanceType(); } assert(exType.isExistentialType()); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f528265ca8910..e77fb51158f8a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3329,6 +3329,11 @@ MetatypeType::MetatypeType(Type T, const ASTContext *C, ExistentialMetatypeType * ExistentialMetatypeType::get(Type T, Optional repr, const ASTContext &ctx) { + // If we're creating an existential metatype from an + // existential type, wrap the constraint type direcly. + if (auto existential = T->getAs()) + T = existential->getConstraintType(); + auto properties = T->getRecursiveProperties(); auto arena = getArena(properties); @@ -3361,6 +3366,18 @@ ExistentialMetatypeType::ExistentialMetatypeType(Type T, } } +Type ExistentialMetatypeType::getExistentialInstanceType() { + auto instanceType = getInstanceType(); + // Note that Any and AnyObject don't yet use ExistentialType. + if (getASTContext().LangOpts.EnableExplicitExistentialTypes && + !instanceType->is() && + !(instanceType->isAny() || instanceType->isAnyObject())) { + instanceType = ExistentialType::get(instanceType); + } + + return instanceType; +} + ModuleType *ModuleType::get(ModuleDecl *M) { ASTContext &C = M->getASTContext(); @@ -4326,7 +4343,7 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const { CanType OpenedArchetypeType::getAny(Type existential) { if (auto metatypeTy = existential->getAs()) { - auto instanceTy = metatypeTy->getInstanceType(); + auto instanceTy = metatypeTy->getExistentialInstanceType(); return CanMetatypeType::get(OpenedArchetypeType::getAny(instanceTy)); } assert(existential->isExistentialType()); diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index f3d88ba6be144..e05a901e6e1b6 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2067,7 +2067,10 @@ class Verifier : public ASTWalker { abort(); } - checkSameType(E->getBase()->getType(), metatype->getInstanceType(), + auto instance = metatype->getInstanceType(); + if (auto existential = metatype->getAs()) + instance = existential->getExistentialInstanceType(); + checkSameType(E->getBase()->getType(), instance, "base type of .Type expression"); verifyCheckedBase(E); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 1046d859eff7c..f947b9edd1b94 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -464,6 +464,8 @@ Type TypeBase::eraseOpenedExistential(OpenedArchetypeType *opened) { auto instanceType = metatypeType->getInstanceType(); if (instanceType->hasOpenedExistential()) { instanceType = instanceType->eraseOpenedExistential(opened); + if (auto existential = instanceType->getAs()) + instanceType = existential->getConstraintType(); return ExistentialMetatypeType::get(instanceType); } } @@ -1199,6 +1201,9 @@ Type TypeBase::replaceSelfParameterType(Type newSelf) { /// Look through a metatype, or just return the original type if it is /// not a metatype. Type TypeBase::getMetatypeInstanceType() { + if (auto existentialMetaType = getAs()) + return existentialMetaType->getExistentialInstanceType(); + if (auto metaTy = getAs()) return metaTy->getInstanceType(); diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index fe33017a83692..a81dcfd3fa25a 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2960,8 +2960,19 @@ class SILVerifier : public SILVerifierBase { "value_metatype instruction must have a metatype representation"); require(MI->getOperand()->getType().isAnyExistentialType(), "existential_metatype operand must be of protocol type"); + + // The result of an existential_metatype instruction is an existential + // metatype with the same constraint type as its existential operand. auto formalInstanceTy = MI->getType().castTo().getInstanceType(); + if (M->getASTContext().LangOpts.EnableExplicitExistentialTypes && + formalInstanceTy->isConstraintType() && + !(formalInstanceTy->isAny() || formalInstanceTy->isAnyObject())) { + require(MI->getOperand()->getType().is(), + "existential_metatype operand must be an existential type"); + formalInstanceTy = + ExistentialType::get(formalInstanceTy)->getCanonicalType(); + } require(isLoweringOf(MI->getOperand()->getType(), formalInstanceTy), "existential_metatype result must be formal metatype of " "lowered operand type"); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index c25e26806628c..7f6c109adb83c 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -220,7 +220,7 @@ static ManagedValue emitTransformExistential(SILGenFunction &SGF, fromInstanceType = cast(fromInstanceType) .getInstanceType(); toInstanceType = cast(toInstanceType) - .getInstanceType(); + ->getExistentialInstanceType()->getCanonicalType(); } ArrayRef conformances = diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 9824e6bd3825d..480d93d2374dc 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1379,6 +1379,8 @@ namespace { baseIsInstance = false; isExistentialMetatype = baseMeta->is(); baseTy = baseMeta->getInstanceType(); + if (auto existential = baseTy->getAs()) + baseTy = existential->getConstraintType(); // A valid reference to a static member (computed property or a method) // declared on a protocol is only possible if result type conforms to @@ -5346,10 +5348,8 @@ Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType) { while (fromInstanceType->is() && toInstanceType->is()) { - fromInstanceType = fromInstanceType->castTo() - ->getInstanceType(); - toInstanceType = toInstanceType->castTo() - ->getInstanceType(); + fromInstanceType = fromInstanceType->getMetatypeInstanceType(); + toInstanceType = toInstanceType->getMetatypeInstanceType(); } if (fromInstanceType->is()) { @@ -5422,7 +5422,7 @@ Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType) { toInstanceType->is()) { if (!fromInstanceType->is()) fromInstanceType = fromInstanceType->castTo()->getInstanceType(); - toInstanceType = toInstanceType->castTo()->getInstanceType(); + toInstanceType = toInstanceType->castTo()->getExistentialInstanceType(); } ASTContext &ctx = cs.getASTContext(); @@ -7511,7 +7511,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, openedInstanceTy = metaTy->getInstanceType(); existentialInstanceTy = existentialInstanceTy ->castTo() - ->getInstanceType(); + ->getExistentialInstanceType(); } assert(openedInstanceTy->castTo() ->getOpenedExistentialType() diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 5544a8d065a23..4294c6c78bb5a 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1995,8 +1995,12 @@ ConstraintSystem::getTypeOfMemberReference( if (t->isEqual(selfTy)) return baseObjTy; if (auto *metatypeTy = t->getAs()) - if (metatypeTy->getInstanceType()->isEqual(selfTy)) - return ExistentialMetatypeType::get(baseObjTy); + if (metatypeTy->getInstanceType()->isEqual(selfTy)) { + auto constraint = baseObjTy; + if (auto existential = baseObjTy->getAs()) + constraint = existential->getConstraintType(); + return ExistentialMetatypeType::get(constraint); + } return t; }); } diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index f82e45f3ee30b..ec390422e8dcd 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-explicit-existential-types protocol Fooable { func foo() } protocol Barable { func bar() } diff --git a/test/SILGen/protocol_with_superclass.swift b/test/SILGen/protocol_with_superclass.swift index 8f3c86bc2f9bc..e389e5c9a59a5 100644 --- a/test/SILGen/protocol_with_superclass.swift +++ b/test/SILGen/protocol_with_superclass.swift @@ -1,6 +1,9 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s // RUN: %target-swift-frontend -emit-ir %s +// RUN: %target-swift-emit-silgen -enable-explicit-existential-types %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir -enable-explicit-existential-types %s + // Protocols with superclass-constrained Self. class Concrete { diff --git a/test/SILGen/subclass_existentials.swift b/test/SILGen/subclass_existentials.swift index 7141a054bfe8b..e0d1de9bf6f27 100644 --- a/test/SILGen/subclass_existentials.swift +++ b/test/SILGen/subclass_existentials.swift @@ -2,6 +2,9 @@ // RUN: %target-swift-emit-silgen -module-name subclass_existentials -Xllvm -sil-full-demangle -parse-as-library -primary-file %s -verify | %FileCheck %s // RUN: %target-swift-emit-ir -module-name subclass_existentials -parse-as-library -primary-file %s +// RUN: %target-swift-emit-silgen -module-name subclass_existentials -Xllvm -sil-full-demangle -parse-as-library -enable-explicit-existential-types -primary-file %s -verify | %FileCheck %s +// RUN: %target-swift-emit-ir -module-name subclass_existentials -parse-as-library -enable-explicit-existential-types -primary-file %s + // Note: we pass -verify above to ensure there are no spurious // compiler warnings relating to casts. From b7c634849d33b051d90da5852fce437320fd36c2 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 01:11:08 -0800 Subject: [PATCH 14/24] [Sema] Implement type join for existential types. --- lib/AST/TypeJoinMeet.cpp | 24 ++++++++++++++++++++++++ test/Sema/type_join.swift | 1 + 2 files changed, 25 insertions(+) diff --git a/lib/AST/TypeJoinMeet.cpp b/lib/AST/TypeJoinMeet.cpp index 403eb0417dc83..c40e424960014 100644 --- a/lib/AST/TypeJoinMeet.cpp +++ b/lib/AST/TypeJoinMeet.cpp @@ -66,6 +66,7 @@ struct TypeJoin : CanTypeVisitor { CanType visitBoundGenericStructType(CanType second); CanType visitMetatypeType(CanType second); CanType visitExistentialMetatypeType(CanType second); + CanType visitExistentialType(CanType second); CanType visitModuleType(CanType second); CanType visitDynamicSelfType(CanType second); CanType visitArchetypeType(CanType second); @@ -271,6 +272,29 @@ CanType TypeJoin::visitExistentialMetatypeType(CanType second) { return ExistentialMetatypeType::get(joinInstance)->getCanonicalType(); } +CanType TypeJoin::visitExistentialType(CanType second) { + assert(First != second); + + if (First->getKind() != second->getKind()) + return TheAnyType; + + auto firstConstraint = First->castTo() + ->getConstraintType()->getCanonicalType(); + auto secondConstraint = second->castTo() + ->getConstraintType()->getCanonicalType(); + + auto joinInstance = join(firstConstraint, secondConstraint); + if (!joinInstance) + return CanType(); + + if (joinInstance->is() || + joinInstance->isAny() || + joinInstance->isAnyObject()) + return joinInstance; + + return ExistentialType::get(joinInstance)->getCanonicalType(); +} + CanType TypeJoin::visitModuleType(CanType second) { assert(First != second); diff --git a/test/Sema/type_join.swift b/test/Sema/type_join.swift index 3de393ed929e4..8b856bb2f9075 100644 --- a/test/Sema/type_join.swift +++ b/test/Sema/type_join.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -parse-stdlib +// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-explicit-existential-types import Swift From 5dced8e5f9255f6dfe92a1a15b5e5180657500de Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 01:24:39 -0800 Subject: [PATCH 15/24] [ConstraintSystem] Fix a few constraint system corner cases with explicit existential types. --- lib/AST/DiagnosticEngine.cpp | 18 ++++++ lib/Sema/CSDiagnostics.cpp | 38 ++++++++++--- lib/Sema/CSSimplify.cpp | 73 ++++++++++++++----------- lib/Sema/ConstraintSystem.cpp | 3 +- lib/Sema/TypeCheckConstraints.cpp | 13 ++++- test/Constraints/common_type_objc.swift | 3 + test/Constraints/openExistential.swift | 1 + 7 files changed, 106 insertions(+), 43 deletions(-) diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 691c3b7b48166..f27104ed2bdc3 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -549,6 +549,24 @@ static bool typeSpellingIsAmbiguous(Type type, auto argType = arg.getAsType(); if (argType && argType->getWithoutParens().getPointer() != type.getPointer() && argType->getWithoutParens().getString(PO) == type.getString(PO)) { + // Currently, existential types are spelled the same way + // as protocols and compositions. We can remove this once + // existenials are printed with 'any'. + if (type->is() || argType->isExistentialType()) { + auto constraint = type; + if (auto existential = type->getAs()) + constraint = existential->getConstraintType(); + + auto argConstraint = argType; + if (auto existential = argType->getAs()) + argConstraint = existential->getConstraintType(); + + if (constraint.getPointer() != argConstraint.getPointer()) + return true; + + continue; + } + return true; } } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 52b58b3808242..1418b0e7c4a6b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -221,6 +221,10 @@ ValueDecl *RequirementFailure::getDeclRef() const { // diagnostic directly to its declaration without desugaring. if (auto *alias = dyn_cast(type.getPointer())) return alias->getDecl(); + + if (auto existential = type->getAs()) + return existential->getConstraintType()->getAnyGeneric(); + return type->getAnyGeneric(); }; @@ -531,10 +535,14 @@ bool MissingConformanceFailure::diagnoseTypeCannotConform( return false; } + Type constraintType = nonConformingType; + if (auto existential = constraintType->getAs()) + constraintType = existential->getConstraintType(); + emitDiagnostic(diag::type_cannot_conform, nonConformingType->isExistentialType(), nonConformingType, - nonConformingType->isEqual(protocolType), + constraintType->isEqual(protocolType), protocolType); bool emittedSpecializedNote = false; @@ -2328,9 +2336,13 @@ bool ContextualFailure::diagnoseAsError() { if (CTP == CTP_ForEachStmt || CTP == CTP_ForEachSequence) { if (fromType->isAnyExistentialType()) { + Type constraintType = fromType; + if (auto existential = constraintType->getAs()) + constraintType = existential->getConstraintType(); + emitDiagnostic(diag::type_cannot_conform, /*isExistentialType=*/true, fromType, - fromType->isEqual(toType), toType); + constraintType->isEqual(toType), toType); emitDiagnostic(diag::only_concrete_types_conform_to_protocols); return true; } @@ -3063,7 +3075,10 @@ bool ContextualFailure::tryProtocolConformanceFixIt( // the protocols of the composition, then store the composition directly. // This is because we need to append 'Foo & Bar' instead of 'Foo, Bar' in // order to match the written type. - if (auto compositionTy = unwrappedToType->getAs()) { + auto constraint = unwrappedToType; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + if (auto compositionTy = constraint->getAs()) { if (compositionTy->getMembers().size() == missingProtoTypeStrings.size()) { missingProtoTypeStrings = {compositionTy->getString()}; } @@ -3651,7 +3666,8 @@ bool MissingMemberFailure::diagnoseAsError() { diagnostic.highlight(getSourceRange()) .highlight(nameLoc.getSourceRange()); correction->addFixits(diagnostic); - } else if (instanceTy->getAnyNominal() && + } else if ((instanceTy->getAnyNominal() || + instanceTy->is()) && getName().getBaseName() == DeclBaseName::createConstructor()) { auto &cs = getConstraintSystem(); @@ -4123,7 +4139,11 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { // If we are in a protocol extension of 'Proto' and we see // 'Proto.static', suggest 'Self.static' if (auto extensionContext = parent->getExtendedProtocolDecl()) { - if (extensionContext->getDeclaredType()->isEqual(instanceTy)) { + auto constraint = instanceTy; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + + if (extensionContext->getDeclaredType()->isEqual(constraint)) { Diag->fixItReplace(getSourceRange(), "Self"); } } @@ -6838,10 +6858,14 @@ bool AssignmentTypeMismatchFailure::diagnoseMissingConformance() const { auto retrieveProtocols = [](Type type, llvm::SmallPtrSetImpl &members) { - if (auto *protocol = type->getAs()) + auto constraint = type; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + + if (auto *protocol = constraint->getAs()) members.insert(protocol->getDecl()); - if (auto *composition = type->getAs()) { + if (auto *composition = constraint->getAs()) { for (auto member : composition->getMembers()) { if (auto *protocol = member->getAs()) members.insert(protocol->getDecl()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 172e31d7ce545..a62ed897ab032 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2886,42 +2886,40 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2, return result; } - // Handle protocol compositions. - if (auto existential1 = type1->getAs()) { - if (auto existential2 = type2->getAs()) { - auto layout1 = existential1->getExistentialLayout(); - auto layout2 = existential2->getExistentialLayout(); - - // Explicit AnyObject and protocols must match exactly. - if (layout1.hasExplicitAnyObject != layout2.hasExplicitAnyObject) - return getTypeMatchFailure(locator); + // Handle existential types. + if (type1->isExistentialType() && type2->isExistentialType()) { + auto layout1 = type1->getExistentialLayout(); + auto layout2 = type2->getExistentialLayout(); - if (layout1.getProtocols().size() != layout2.getProtocols().size()) - return getTypeMatchFailure(locator); + // Explicit AnyObject and protocols must match exactly. + if (layout1.hasExplicitAnyObject != layout2.hasExplicitAnyObject) + return getTypeMatchFailure(locator); - for (unsigned i: indices(layout1.getProtocols())) { - if (!layout1.getProtocols()[i]->isEqual(layout2.getProtocols()[i])) - return getTypeMatchFailure(locator); - } + if (layout1.getProtocols().size() != layout2.getProtocols().size()) + return getTypeMatchFailure(locator); - // This is the only interesting case. We might have type variables - // on either side of the superclass constraint, so make sure we - // recursively call matchTypes() here. - if (layout1.explicitSuperclass || layout2.explicitSuperclass) { - if (!layout1.explicitSuperclass || !layout2.explicitSuperclass) - return getTypeMatchFailure(locator); + for (unsigned i: indices(layout1.getProtocols())) { + if (!layout1.getProtocols()[i]->isEqual(layout2.getProtocols()[i])) + return getTypeMatchFailure(locator); + } - auto result = matchTypes(layout1.explicitSuperclass, - layout2.explicitSuperclass, - ConstraintKind::Bind, subflags, - locator.withPathElement( - ConstraintLocator::ExistentialSuperclassType)); - if (result.isFailure()) - return result; - } + // This is the only interesting case. We might have type variables + // on either side of the superclass constraint, so make sure we + // recursively call matchTypes() here. + if (layout1.explicitSuperclass || layout2.explicitSuperclass) { + if (!layout1.explicitSuperclass || !layout2.explicitSuperclass) + return getTypeMatchFailure(locator); - return getTypeMatchSuccess(); + auto result = matchTypes(layout1.explicitSuperclass, + layout2.explicitSuperclass, + ConstraintKind::Bind, subflags, + locator.withPathElement( + ConstraintLocator::ExistentialSuperclassType)); + if (result.isFailure()) + return result; } + + return getTypeMatchSuccess(); } // Handle nominal types that are not directly generic. if (auto nominal1 = type1->getAs()) { @@ -6033,8 +6031,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, return true; return false; }; + + auto constraintType = meta1->getInstanceType(); + if (auto existential = constraintType->getAs()) + constraintType = existential->getConstraintType(); - if (auto protoTy = meta1->getInstanceType()->getAs()) { + if (auto protoTy = constraintType->getAs()) { if (protoTy->getDecl()->isObjC() && isProtocolClassType(type2)) { increaseScore(ScoreKind::SK_UserConversion); @@ -7007,7 +7009,12 @@ static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType, if (!nilLiteral) return false; - return toType->isEqual(nilLiteral->getDeclaredType()) && + auto nilLiteralType = nilLiteral->getDeclaredType(); + if (ctx.LangOpts.EnableExplicitExistentialTypes) { + nilLiteralType = ExistentialType::get(nilLiteralType); + } + + return toType->isEqual(nilLiteralType) && fromType->getOptionalObjectType(); } @@ -9808,7 +9815,7 @@ ConstraintSystem::simplifyOpenedExistentialOfConstraint( auto instanceTy = type2; if (auto metaTy = type2->getAs()) { isMetatype = true; - instanceTy = metaTy->getInstanceType(); + instanceTy = metaTy->getExistentialInstanceType(); } assert(instanceTy->isExistentialType()); Type openedTy = OpenedArchetypeType::get(instanceTy); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 4294c6c78bb5a..1f6bbe825db30 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2150,7 +2150,8 @@ Type ConstraintSystem::getEffectiveOverloadType(ConstraintLocator *locator, } else if (isa(decl) || isa(decl)) { if (decl->isInstanceMember() && (!overload.getBaseType() || - !overload.getBaseType()->getAnyNominal())) + (!overload.getBaseType()->getAnyNominal() && + !overload.getBaseType()->is()))) return Type(); // Cope with 'Self' returns. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1224ebd7bce03..8d0aa0d38d5ae 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1712,13 +1712,16 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // } // } // + auto constraint = fromType; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); if (auto *protocolDecl = - dyn_cast_or_null(fromType->getAnyNominal())) { + dyn_cast_or_null(constraint->getAnyNominal())) { if (!couldDynamicallyConformToProtocol(toType, protocolDecl, module)) { return failed(); } } else if (auto protocolComposition = - fromType->getAs()) { + constraint->getAs()) { if (llvm::any_of(protocolComposition->getMembers(), [&](Type protocolType) { if (auto protocolDecl = dyn_cast_or_null( @@ -1973,6 +1976,12 @@ static bool checkForDynamicAttribute(CanType ty, } } + // If this is an existential type, check if its constraint type + // has the attribute. + if (auto existential = ty->getAs()) { + return hasAttribute(existential->getConstraintType()); + } + // If this is a protocol composition, check if any of its members have the // attribute. if (auto protocolComp = dyn_cast(ty)) { diff --git a/test/Constraints/common_type_objc.swift b/test/Constraints/common_type_objc.swift index 35a7239ae08cf..94596747ae77b 100644 --- a/test/Constraints/common_type_objc.swift +++ b/test/Constraints/common_type_objc.swift @@ -1,6 +1,9 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s -debug-constraints 2>%t.err // RUN: %FileCheck %s < %t.err +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -enable-explicit-existential-types %s -debug-constraints 2>%t.err +// RUN: %FileCheck %s < %t.err + // REQUIRES: objc_interop import Foundation diff --git a/test/Constraints/openExistential.swift b/test/Constraints/openExistential.swift index a50d1ef26daea..48dcfb383df5b 100644 --- a/test/Constraints/openExistential.swift +++ b/test/Constraints/openExistential.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-explicit-existential-types protocol P { } From f01832819456f33dbe05df0e77a5d80f238c7036 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 09:51:39 -0800 Subject: [PATCH 16/24] [TypeResolver] Fix a few corner cases in type resolution for explicit existential types. Use the correct type resolver context when resolving SIL metatypes and function types, and diagnose an error if an explicit existential type appears in a protocol composition. --- lib/Sema/TypeCheckType.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index dbc20c0f9fada..a42ce813b7f84 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2274,7 +2274,13 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, Optional storedRepr; // The instance type is not a SIL type. auto instanceOptions = options; - instanceOptions.setContext(None); + TypeResolverContext context = TypeResolverContext::None; + if (isa(repr)) { + context = TypeResolverContext::MetatypeBase; + } else if (isa(repr)) { + context = TypeResolverContext::ProtocolMetatypeBase; + } + instanceOptions.setContext(context); instanceOptions -= TypeResolutionFlags::SILType; auto instanceTy = resolveType(base, instanceOptions); @@ -3166,7 +3172,8 @@ NeverNullType TypeResolver::resolveSILFunctionType( ProtocolConformanceRef witnessMethodConformance; if (witnessMethodProtocol) { - auto resolved = resolveType(witnessMethodProtocol, options); + auto resolved = resolveType(witnessMethodProtocol, + options.withContext(TypeResolverContext::GenericRequirement)); if (resolved->hasError()) return resolved; @@ -3443,9 +3450,7 @@ TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, } // FIXME: Don't use ExistentialType for AnyObject for now. - bool isConstraintType = (result->is() && - !result->isAnyObject()); - if (isConstraintType && + if (result->isConstraintType() && !result->isAnyObject() && getASTContext().LangOpts.EnableExplicitExistentialTypes && options.isConstraintImplicitExistential()) { return ExistentialType::get(result); @@ -3807,7 +3812,7 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, continue; } - if (ty->isExistentialType()) { + if (ty->isConstraintType()) { auto layout = ty->getExistentialLayout(); if (auto superclass = layout.explicitSuperclass) if (checkSuperclass(tyR->getStartLoc(), superclass)) From 6e6ca13268069cb67c0e04d48b35ce7cd3b4bacc Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 13:46:35 -0800 Subject: [PATCH 17/24] [Type System] Use the constraint type of an existential type in various places that expect ProtocolType or ProtocoolCompositionType. --- include/swift/AST/ProtocolConformance.h | 2 +- include/swift/AST/Types.h | 5 +++++ lib/APIDigester/ModuleAnalyzerNodes.cpp | 6 ++++++ lib/AST/Decl.cpp | 5 ++++- lib/AST/Type.cpp | 24 +++++++++++++++++++----- lib/IRGen/DebugTypeInfo.cpp | 2 ++ lib/IRGen/IRGenMangler.cpp | 2 ++ lib/SIL/Utils/DynamicCasts.cpp | 3 +-- lib/SILGen/SILGenConvert.cpp | 7 +++++-- lib/SILGen/SILGenType.cpp | 5 ++++- lib/Sema/TypeCheckAccess.cpp | 7 ++++++- lib/Sema/TypeCheckDecl.cpp | 7 +++++++ lib/Sema/TypeCheckProtocol.cpp | 10 ++++++++-- 13 files changed, 70 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index d84459e1e7904..714349cfdcb18 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -660,7 +660,7 @@ class SelfProtocolConformance : public RootProtocolConformance { public: /// Get the protocol being conformed to. ProtocolDecl *getProtocol() const { - return getType()->castTo()->getDecl(); + return dyn_cast(getType()->getAnyNominal()); } /// Get the declaration context in which this conformance was declared. diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 32b0bd51fac1d..41026850b28a3 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -6455,6 +6455,8 @@ inline NominalTypeDecl *TypeBase::getNominalOrBoundGenericNominal() { inline NominalTypeDecl *CanType::getNominalOrBoundGenericNominal() const { if (auto Ty = dyn_cast(*this)) return Ty->getDecl(); + if (auto Ty = dyn_cast(*this)) + return Ty->getConstraintType()->getNominalOrBoundGenericNominal(); return nullptr; } @@ -6463,6 +6465,9 @@ inline NominalTypeDecl *TypeBase::getAnyNominal() { } inline Type TypeBase::getNominalParent() { + if (auto existential = getAs()) + return existential->getConstraintType()->getNominalParent(); + return castTo()->getParent(); } diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index e90da097f4313..6d05f5bd1d718 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -1017,6 +1017,12 @@ static StringRef getTypeName(SDKContext &Ctx, Type Ty, if (auto *NAT = dyn_cast(Ty.getPointer())) { return NAT->getDecl()->getNameStr(); } + + if (auto existential = Ty->getAs()) { + return getTypeName(Ctx, existential->getConstraintType(), + IsImplicitlyUnwrappedOptional); + } + if (Ty->getAnyNominal()) { if (IsImplicitlyUnwrappedOptional) { assert(Ty->getOptionalObjectType()); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 39547a9e1aa9e..dd7995158f2dd 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5164,7 +5164,10 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type, return SelfReferenceInfo::forSelfRef(SelfReferencePosition::Invariant); // Protocol compositions preserve variance. - if (auto *comp = type->getAs()) { + auto constraint = type; + if (auto existential = constraint->getAs()) + constraint = existential->getConstraintType(); + if (auto *comp = constraint->getAs()) { // 'Self' may be referenced only in a superclass component. if (const auto superclass = comp->getSuperclass()) { return findProtocolSelfReferences(proto, superclass, position); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f947b9edd1b94..84946ea475bb2 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -104,6 +104,8 @@ NominalTypeDecl *CanType::getAnyNominal() const { } GenericTypeDecl *CanType::getAnyGeneric() const { + if (auto existential = dyn_cast(*this)) + return existential->getConstraintType()->getAnyGeneric(); if (auto Ty = dyn_cast(*this)) return Ty->getDecl(); return nullptr; @@ -2643,7 +2645,7 @@ getForeignRepresentable(Type type, ForeignLanguage language, // If type has an error let's fail early. if (type->hasError()) return failure(); - + // Look through one level of optional type, but remember that we did. bool wasOptional = false; if (auto valueType = type->getOptionalObjectType()) { @@ -2651,6 +2653,9 @@ getForeignRepresentable(Type type, ForeignLanguage language, wasOptional = true; } + if (auto existential = type->getAs()) + type = existential->getConstraintType(); + // Objective-C object types, including metatypes. if (language == ForeignLanguage::ObjectiveC) { auto representable = getObjCObjectRepresentable(type, dc); @@ -3242,9 +3247,14 @@ Type ArchetypeType::getExistentialType() const { for (auto proto : getConformsTo()) { constraintTypes.push_back(proto->getDeclaredInterfaceType()); } - return ProtocolCompositionType::get( - const_cast(this)->getASTContext(), constraintTypes, - requiresClass()); + auto &ctx = const_cast(this)->getASTContext(); + auto constraint = ProtocolCompositionType::get( + ctx, constraintTypes, requiresClass()); + + if (ctx.LangOpts.EnableExplicitExistentialTypes) + return ExistentialType::get(constraint); + + return constraint; } PrimaryArchetypeType::PrimaryArchetypeType(const ASTContext &Ctx, @@ -5555,6 +5565,10 @@ bool Type::isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic) const { if (!Ty) return false; + if (auto existential = dyn_cast(Ty.getPointer())) + return existential->getConstraintType() + .isPrivateStdlibType(treatNonBuiltinProtocolsAsPublic); + // A 'public' typealias can have an 'internal' type. if (auto *NAT = dyn_cast(Ty.getPointer())) { auto *AliasDecl = NAT->getDecl(); @@ -5739,7 +5753,7 @@ SILBoxType::SILBoxType(ASTContext &C, Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { assert(isAnyExistentialType()); if (auto metaty = getAs()) { - opened = OpenedArchetypeType::get(metaty->getInstanceType()); + opened = OpenedArchetypeType::get(metaty->getExistentialInstanceType()); if (metaty->hasRepresentation()) return MetatypeType::get(opened, metaty->getRepresentation()); else diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index 779f30d9c782a..8ef7826cc4762 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -158,6 +158,8 @@ TypeDecl *DebugTypeInfo::getDecl() const { return UBG->getDecl(); if (auto *BG = dyn_cast(Type)) return BG->getDecl(); + if (auto *E = dyn_cast(Type)) + return E->getConstraintType()->getAnyNominal(); return nullptr; } diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index ea92243bb26b8..b1555583bf151 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -216,6 +216,8 @@ std::string IRGenMangler::mangleTypeForLLVMTypeName(CanType Ty) { // To make LLVM IR more readable we always add a 'T' prefix so that type names // don't start with a digit and don't need to be quoted. Buffer << 'T'; + if (auto existential = Ty->getAs()) + Ty = existential->getConstraintType()->getCanonicalType(); if (auto P = dyn_cast(Ty)) { appendProtocolName(P->getDecl(), /*allowStandardSubstitution=*/false); appendOperator("P"); diff --git a/lib/SIL/Utils/DynamicCasts.cpp b/lib/SIL/Utils/DynamicCasts.cpp index 550a0794184b2..ae167f9c9ec57 100644 --- a/lib/SIL/Utils/DynamicCasts.cpp +++ b/lib/SIL/Utils/DynamicCasts.cpp @@ -468,8 +468,7 @@ swift::classifyDynamicCast(ModuleDecl *M, : DynamicCastFeasibility::WillFail; } - if (targetMetatype.isAnyExistentialType() && - (isa(target) || isa(target))) { + if (targetMetatype.isAnyExistentialType() && target->isExistentialType()) { auto Feasibility = classifyDynamicCastToProtocol(M, source, target, isWholeModuleOpts); // Cast from existential metatype to existential metatype may still diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 20b353e50f822..8ec0c67263735 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -913,8 +913,11 @@ ManagedValue SILGenFunction::emitExistentialMetatypeToObject(SILLocation loc, ManagedValue SILGenFunction::emitProtocolMetatypeToObject(SILLocation loc, CanType inputTy, SILType resultTy) { - ProtocolDecl *protocol = inputTy->castTo() - ->getInstanceType()->castTo()->getDecl(); + auto protocolType = inputTy->castTo()->getInstanceType(); + if (auto existential = protocolType->getAs()) + protocolType = existential->getConstraintType(); + + ProtocolDecl *protocol = protocolType->castTo()->getDecl(); SILValue value = B.createObjCProtocol(loc, protocol, resultTy); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 08f2af1512c12..412cbdab5749a 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -842,7 +842,10 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, ProtocolConformanceRef(conformance)); // Open the protocol type. - auto openedType = OpenedArchetypeType::get(protocolType); + Type existential = protocolType; + if (SGM.getASTContext().LangOpts.EnableExplicitExistentialTypes) + existential = ExistentialType::get(protocolType); + auto openedType = OpenedArchetypeType::get(existential); // Form the substitutions for calling the witness. auto witnessSubs = SubstitutionMap::getProtocolSubstitutions(protocol, diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 5b9d1da40891a..e1b1d6813f512 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -783,7 +783,12 @@ class AccessControlChecker : public AccessControlCheckerBase, Type()); } - auto declKindForType = [](Type type) { + auto declKindForType = [](Type type) -> DescriptiveDeclKind { + // If this is an existential type, use the decl kind of + // its constraint type. + if (auto existential = type->getAs()) + type = existential->getConstraintType(); + if (isa(type.getPointer())) return DescriptiveDeclKind::TypeAlias; else if (auto nominal = type->getAnyNominal()) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 08eba2bf58742..c5432dd5cd72a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1899,6 +1899,13 @@ bool swift::isMemberOperator(FuncDecl *decl, Type type) { } if (isProtocol) { + // FIXME: Source compatibility hack for Swift 5. The compiler + // accepts member operators on protocols with existential + // type arguments. We should consider banning this in Swift 6. + if (auto existential = paramType->getAs()) { + return selfNominal == existential->getConstraintType()->getAnyNominal(); + } + // For a protocol, is it the 'Self' type parameter? if (auto genericParam = paramType->getAs()) if (genericParam->isEqual(DC->getSelfInterfaceType())) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 3414728c9a997..f01d84783cdb6 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5273,8 +5273,11 @@ void swift::diagnoseConformanceFailure(Type T, TypeChecker::containsProtocol(T, Proto, DC->getParentModule())) { if (!T->isObjCExistentialType()) { + Type constraintType = T; + if (auto existential = T->getAs()) + constraintType = existential->getConstraintType(); diags.diagnose(ComplainLoc, diag::type_cannot_conform, true, - T, T->isEqual(Proto->getDeclaredInterfaceType()), + T, constraintType->isEqual(Proto->getDeclaredInterfaceType()), Proto->getDeclaredInterfaceType()); diags.diagnose(ComplainLoc, diag::only_concrete_types_conform_to_protocols); @@ -5407,7 +5410,10 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, if (T->isExistentialType()) { // Handle the special case of the Error protocol, which self-conforms // *and* has a witness table. - if (T->isEqual(Proto->getDeclaredInterfaceType()) && + auto constraint = T; + if (auto existential = T->getAs()) + constraint = existential->getConstraintType(); + if (constraint->isEqual(Proto->getDeclaredInterfaceType()) && Proto->requiresSelfConformanceWitnessTable()) { auto &ctx = M->getASTContext(); return ProtocolConformanceRef(ctx.getSelfConformance(Proto)); From 3021071af42470bd845188a12a14681b65ffdb19 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 15:59:39 -0800 Subject: [PATCH 18/24] [ASTPrinter] Add an option that controls whether existential types are printed with the `any` keyword. For now, printing `any` is off by default in order to turn on explicit existential types with minimal changes to the test suite. The option will also allow control over how existential types are printed in Swift interfaces. --- include/swift/AST/DiagnosticsSema.def | 4 ++-- include/swift/AST/PrintOptions.h | 4 ++++ lib/AST/ASTPrinter.cpp | 13 ++++++++----- lib/AST/GenericSignatureBuilder.cpp | 12 ++++++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 63dbe7f1ea7d8..d462311ac6b8f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2441,8 +2441,8 @@ ERROR(protocol_composition_one_class,none, "contains class %1", (Type, Type)) ERROR(requires_conformance_nonprotocol,none, - "type %0 constrained to non-protocol, non-class type %1", - (Type, Type)) + "type %0 constrained to non-protocol, non-class type '%1'", + (Type, StringRef)) NOTE(requires_conformance_nonprotocol_fixit,none, "use '%0 == %1' to require '%0' to be '%1'", (StringRef, StringRef)) diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 78499df4b11ce..8a7a8787b2da3 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -278,6 +278,10 @@ struct PrintOptions { bool PrintImplicitAttrs = true; + /// Whether to print the \c any keyword for existential + /// types. + bool PrintExplicitAny = false; + /// Whether to skip keywords with a prefix of underscore such as __consuming. bool SkipUnderscoredKeywords = false; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index a0dc3450413e3..4f9cdf3a32be2 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4852,6 +4852,9 @@ class TypePrinter : public TypeVisitor { isSimpleUnderPrintOptions(opaque->getExistentialType()); } llvm_unreachable("bad opaque-return-type printing mode"); + } else if (auto existential = dyn_cast(T.getPointer())) { + if (!Options.PrintExplicitAny) + return isSimpleUnderPrintOptions(existential->getConstraintType()); } return T->hasSimpleTypeRepr(); } @@ -5272,9 +5275,7 @@ class TypePrinter : public TypeVisitor { } } - auto &ctx = T->getASTContext(); - if (T->is() && - ctx.LangOpts.EnableExplicitExistentialTypes) + if (T->is() && Options.PrintExplicitAny) Printer << "any "; printWithParensIfNotSimple(T->getInstanceType()); @@ -5282,7 +5283,7 @@ class TypePrinter : public TypeVisitor { // We spell normal metatypes of existential types as .Protocol. if (isa(T) && T->getInstanceType()->isAnyExistentialType() && - !ctx.LangOpts.EnableExplicitExistentialTypes) { + !Options.PrintExplicitAny) { Printer << ".Protocol"; } else { Printer << ".Type"; @@ -5875,7 +5876,9 @@ class TypePrinter : public TypeVisitor { } void visitExistentialType(ExistentialType *T) { - Printer << "any "; + if (Options.PrintExplicitAny) + Printer << "any "; + visit(T->getConstraintType()); } diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 380e0f4dd27a8..7f7e7c4ffff91 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -6293,8 +6293,16 @@ GenericSignatureBuilder::finalize(TypeArrayView genericPar auto source = constraint.source; auto loc = source->getLoc(); + // FIXME: The constraint string is printed directly here because + // the current default is to not print `any` for existential + // types, but this error message is super confusing without `any` + // if the user wrote it explicitly. + PrintOptions options; + options.PrintExplicitAny = true; + auto constraintString = constraintType.getString(options); + Diags.diagnose(loc, diag::requires_conformance_nonprotocol, - subjectType, constraintType); + subjectType, constraintString); auto getNameWithoutSelf = [&](std::string subjectTypeName) { std::string selfSubstring = "Self."; @@ -6312,7 +6320,7 @@ GenericSignatureBuilder::finalize(TypeArrayView genericPar auto subjectTypeName = subjectType.getString(); auto subjectTypeNameWithoutSelf = getNameWithoutSelf(subjectTypeName); Diags.diagnose(loc, diag::requires_conformance_nonprotocol_fixit, - subjectTypeNameWithoutSelf, constraintType.getString()) + subjectTypeNameWithoutSelf, constraintString) .fixItReplace(loc, " == "); } } From ee331a8c012d19c0334bdfbd78720dfa6c6298c9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 7 Jan 2022 22:58:10 -0800 Subject: [PATCH 19/24] [Type System] Enable explicit existential types. --- include/swift/Basic/LangOptions.h | 2 +- lib/Serialization/ModuleFormat.h | 2 +- test/Generics/function_defs.swift | 6 +- test/decl/nested/protocol.swift | 2 +- test/decl/protocol/conforms/inherited.swift | 8 +-- test/decl/protocol/protocols.swift | 10 +-- .../protocols_with_self_or_assoc_reqs.swift | 70 +++++++++---------- .../decl/protocol/recursive_requirement.swift | 4 +- test/stmt/foreach.swift | 2 +- test/type/protocol_types.swift | 38 +++++----- 10 files changed, 72 insertions(+), 72 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 1bfed64e7d81e..3960c4d73e7f0 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -315,7 +315,7 @@ namespace swift { /// Enable support for explicit existential types via the \c any /// keyword. - bool EnableExplicitExistentialTypes = false; + bool EnableExplicitExistentialTypes = true; /// Enable experimental flow-sensitive concurrent captures. bool EnableExperimentalFlowSensitiveConcurrentCaptures = false; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 7af4335eb0faa..1ea761faf1e58 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 = 655; // dealloc_stack_ref +const uint16_t SWIFTMODULE_VERSION_MINOR = 656; // enable explicit existentials /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index 8b5d8499ebfa3..04f2f8e7f9a79 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -34,7 +34,7 @@ func min(_ x: T, y: T) -> T { //===----------------------------------------------------------------------===// func existential(_ t1: T, t2: T, u: U) { - var eqComp : EqualComparable = t1 // Ok + var eqComp : EqualComparable = t1 // expected-warning {{protocol 'EqualComparable' as a type must be explicitly marked as 'any'}} eqComp = u if t1.isEqual(eqComp) {} // expected-error{{cannot convert value of type 'EqualComparable' to expected argument type 'T'}} if eqComp.isEqual(t2) {} // expected-error{{member 'isEqual' cannot be used on value of protocol type 'EqualComparable'; use a generic constraint instead}} @@ -49,11 +49,11 @@ func otherExistential(_ t1: T) { otherEqComp = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp - var otherEqComp2 : OtherEqualComparable // Ok + var otherEqComp2 : any OtherEqualComparable // Ok otherEqComp2 = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp2 - _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} + _ = t1 as any EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} } //===----------------------------------------------------------------------===// diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index f6f261bfdd35d..2bda50bf6ee46 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -31,7 +31,7 @@ protocol OuterProtocol { struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int - func f() { let _ = InnerProtocol.self } // Ok + func f() { let _ = InnerProtocol.self } // expected-warning {{protocol 'InnerProtocol' as a type must be explicitly marked as 'any'}} } protocol Racoon { diff --git a/test/decl/protocol/conforms/inherited.swift b/test/decl/protocol/conforms/inherited.swift index 33e50e5ee418b..8b54508922117 100644 --- a/test/decl/protocol/conforms/inherited.swift +++ b/test/decl/protocol/conforms/inherited.swift @@ -167,13 +167,13 @@ class B : A { } func testB(_ b: B) { - var _: P1 = b - var _: P4 = b + var _: P1 = b // expected-warning {{protocol 'P1' as a type must be explicitly marked as 'any'}} + var _: P4 = b // expected-warning {{protocol 'P4' as a type must be explicitly marked as 'any'}} var _: P5 = b var _: P6 = b - var _: P7 = b + var _: P7 = b // expected-warning {{protocol 'P7' as a type must be explicitly marked as 'any'}} var _: P8 = b - var _: P9 = b + var _: P9 = b // expected-warning {{protocol 'P9' as a type must be explicitly marked as 'any'}} } // Class A5 conforms to P5 in an inheritable manner. diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index 89a12f2f7cf1f..c002d33a1f6e1 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -119,7 +119,7 @@ struct Circle { func testCircular(_ circle: Circle) { // FIXME: It would be nice if this failure were suppressed because the protocols // have circular definitions. - _ = circle as CircleStart // expected-error{{value of type 'Circle' does not conform to 'CircleStart' in coercion}} + _ = circle as any CircleStart // expected-error{{value of type 'Circle' does not conform to 'CircleStart' in coercion}} } // @@ -439,17 +439,17 @@ func g(_ x : T) { class C3 : P1 {} // expected-error{{type 'C3' does not conform to protocol 'P1'}} func h(_ x : T) { - _ = x as P1 + _ = x as any P1 } func i(_ x : T?) -> Bool { - return x is P1 + return x is any P1 // expected-warning@-1 {{checking a value with optional type 'T?' against type 'P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} } func j(_ x : C1) -> Bool { - return x is P1 + return x is P1 // expected-warning {{protocol 'P1' as a type must be explicitly marked as 'any'}} } func k(_ x : C1?) -> Bool { - return x is P1 + return x is any P1 } diff --git a/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift b/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift index 05d6410ead2fa..33fac5d434d45 100644 --- a/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift +++ b/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift @@ -57,7 +57,7 @@ protocol P1 { func invariantSelf7(_: (G) -> Void) func invariantSelf8(_: G<(Self) -> Void>) func invariantSelf9(_: G<() -> Self>) - func invariantSelf10(_: P1 & C) + func invariantSelf10(_: any P1 & C) func invariantSelf11() -> G.InnerG func invariantAssoc1(_: inout Q) func invariantAssoc2(_: (inout Q) -> Void) @@ -68,7 +68,7 @@ protocol P1 { func invariantAssoc7(_: (G) -> Void) func invariantAssoc8(_: G<(Q) -> Void>) func invariantAssoc9(_: G<() -> Q>) - func invariantAssoc10(_: P1 & C) + func invariantAssoc10(_: any P1 & C) func invariantAssoc11() -> G.InnerG // Properties @@ -117,7 +117,7 @@ protocol P1 { var invariantSelfProp7: ((G) -> Void) -> Void { get } var invariantSelfProp8: (G<(Self) -> Void>) -> Void { get } var invariantSelfProp9: (G<() -> Self>) -> Void { get } - var invariantSelfProp10: (P1 & C) -> Void { get } + var invariantSelfProp10: (any P1 & C) -> Void { get } var invariantSelfProp11: G.InnerG { get } var invariantAssocProp1: (inout Q) -> Void { get } var invariantAssocProp2: ((inout Q) -> Void) -> Void { get } @@ -128,7 +128,7 @@ protocol P1 { var invariantAssocProp7: ((G) -> Void) { get } var invariantAssocProp8: (G<(Q) -> Void>) { get } var invariantAssocProp9: (G<() -> Q>) -> Void { get } - var invariantAssocProp10: (P1 & C) -> Void { get } + var invariantAssocProp10: (any P1 & C) -> Void { get } var invariantAssocProp11: G.InnerG { get } // Subscripts @@ -172,7 +172,7 @@ protocol P1 { subscript(invariantSelfSubscript4 _: (G) -> Void) -> Void { get } subscript(invariantSelfSubscript5 _: G<(Self) -> Void>) -> Void { get } subscript(invariantSelfSubscript6 _: G<() -> Self>) -> Void { get } - subscript(invariantSelfSubscript7 _: P1 & C) -> Void { get } + subscript(invariantSelfSubscript7 _: any P1 & C) -> Void { get } subscript(invariantSelfSubscript8 _: Void) -> G.InnerG { get } subscript(invariantAssocSubscript1 _: G) -> Void { get } subscript(invariantAssocSubscript2 _: Void) -> G { get } @@ -180,7 +180,7 @@ protocol P1 { subscript(invariantAssocSubscript4 _: (G) -> Void) -> Void { get } subscript(invariantAssocSubscript5 _: G<(Q) -> Void>) -> Void { get } subscript(invariantAssocSubscript6 _: G<() -> Q>) -> Void { get } - subscript(invariantAssocSubscript7 _: P1 & C) -> Void { get } + subscript(invariantAssocSubscript7 _: any P1 & C) -> Void { get } subscript(invariantAssocSubscript8 _: Void) -> G.InnerG { get } } @available(macOS 10.15, *) @@ -191,40 +191,40 @@ extension P1 { } do { - func testP1(arg: P1) { + func testP1(arg: any P1) { _ = arg.covariantSelfSimple() // ok - let _: P1 = arg.covariantSelfSimple() // ok + let _: any P1 = arg.covariantSelfSimple() // ok _ = arg.covariantSelfComplex({ _ in }, { _ in }, { _ in }, { _ in }) // ok - let _: [String : () -> (P1, P1)] = arg.covariantSelfComplex( - { (x: P1) in }, - { (x: P1?) in }, - { (x: Array) in }, - { (x: Array?>) in } + let _: [String : () -> (any P1, any P1)] = arg.covariantSelfComplex( + { (x: any P1) in }, + { (x: (any P1)?) in }, + { (x: Array) in }, + { (x: Array?>) in } ) // ok arg.covariantAssocSimple // expected-error {{member 'covariantAssocSimple' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} arg.covariantAssocComplex({ _ in }, { _ in }, { _ in }, { _ in }) // expected-error {{member 'covariantAssocComplex' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} // FIXME: expected-error@-1 {{unable to infer type of a closure parameter '_' in the current context}} _ = arg.covariantSelfPropSimple // ok - let _: P1 = arg.covariantSelfPropSimple // ok + let _: any P1 = arg.covariantSelfPropSimple // ok _ = arg.covariantSelfPropComplex // ok let _: ( - _: (P1) -> Void, - _: (P1?) -> Void, - _: (Array) -> Void, - _: (Array?>) -> Void - ) -> [String : () -> (P1, P1)] = arg.covariantSelfPropComplex // ok + _: (any P1) -> Void, + _: ((any P1)?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (any P1, any P1)] = arg.covariantSelfPropComplex // ok arg.covariantAssocPropSimple // expected-error {{member 'covariantAssocPropSimple' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} arg.covariantAssocPropComplex // expected-error {{member 'covariantAssocPropComplex' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} _ = arg[covariantSelfSubscriptSimple: ()] // ok - let _: P1 = arg[covariantSelfSubscriptSimple: ()] // ok + let _: any P1 = arg[covariantSelfSubscriptSimple: ()] // ok _ = arg[covariantSelfSubscriptComplex: { _ in }, { _ in }, { _ in }, { _ in }] // ok - let _: [String : () -> (P1, P1)] = arg[ - covariantSelfSubscriptComplex: { (x: P1) in }, - { (x: P1?) in }, - { (x: Array) in }, - { (x: Array?>) in } + let _: [String : () -> (any P1, any P1)] = arg[ + covariantSelfSubscriptComplex: { (x: any P1) in }, + { (x: (any P1)?) in }, + { (x: Array) in }, + { (x: Array?>) in } ] // ok arg[covariantAssocSubscriptSimple: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} arg[covariantAssocSubscriptComplex: { _ in }, { _ in }, { _ in }, { _ in }] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} @@ -444,15 +444,15 @@ protocol P1_TypeMemberOnInstanceAndViceVersa { do { // Test that invalid reference errors prevail over unsupported existential // member accesses. - func test(protoMeta: P1_TypeMemberOnInstanceAndViceVersa.Protocol, - existMeta: P1_TypeMemberOnInstanceAndViceVersa.Type, - instance: P1_TypeMemberOnInstanceAndViceVersa) { + func test(protoMeta: (any P1_TypeMemberOnInstanceAndViceVersa).Type, + existMeta: any P1_TypeMemberOnInstanceAndViceVersa.Type, + instance: any P1_TypeMemberOnInstanceAndViceVersa) { // P1_TypeMemberOnInstanceAndViceVersa.Protocol - protoMeta.static_invariantSelfMethod() // expected-error {{static member 'static_invariantSelfMethod' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} - protoMeta.static_invariantSelfProp // expected-error {{static member 'static_invariantSelfProp' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} - protoMeta[static_invariantSelfSubscript: ()] // expected-error {{static member 'subscript' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} + protoMeta.static_invariantSelfMethod() // expected-error {{static member 'static_invariantSelfMethod' cannot be used on protocol metatype '(P1_TypeMemberOnInstanceAndViceVersa).Protocol'}} + protoMeta.static_invariantSelfProp // expected-error {{static member 'static_invariantSelfProp' cannot be used on protocol metatype '(P1_TypeMemberOnInstanceAndViceVersa).Protocol'}} + protoMeta[static_invariantSelfSubscript: ()] // expected-error {{static member 'subscript' cannot be used on protocol metatype '(P1_TypeMemberOnInstanceAndViceVersa).Protocol'}} _ = protoMeta.covariantSelfMethod // ok - protoMeta.invariantSelfMethod // expected-error {{member 'invariantSelfMethod' cannot be used on value of protocol type 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'; use a generic constraint instead}} + protoMeta.invariantSelfMethod // expected-error {{member 'invariantSelfMethod' cannot be used on value of protocol type '(P1_TypeMemberOnInstanceAndViceVersa).Protocol'; use a generic constraint instead}} protoMeta.invariantSelfProp // expected-error {{instance member 'invariantSelfProp' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} protoMeta[invariantSelfSubscript: ()] // expected-error {{instance member 'subscript' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} @@ -481,7 +481,7 @@ protocol P2 { var prop: Self { get set } } -func takesP2(p2: P2) { +func takesP2(p2: any P2) { _ = p2[] // expected-error@-1{{member 'subscript' cannot be used on value of protocol type 'P2'; use a generic constraint instead}} _ = p2.prop @@ -500,7 +500,7 @@ protocol MiscTestsProto { subscript(intToInt _: Int) -> Int { get } } do { - func miscTests(_ arg: MiscTestsProto) { // ok + func miscTests(_ arg: any MiscTestsProto) { // ok arg.runce(5) do { @@ -515,7 +515,7 @@ do { _ = arg[intToAssoc: 17] // expected-error{{member 'subscript' cannot be used on value of protocol type 'MiscTestsProto'; use a generic constraint instead}} } - func existentialSequence(_ e: Sequence) { + func existentialSequence(_ e: any Sequence) { var x = e.makeIterator() // expected-error{{member 'makeIterator' cannot be used on value of protocol type 'Sequence'; use a generic constraint instead}} x.next() x.nonexistent() diff --git a/test/decl/protocol/recursive_requirement.swift b/test/decl/protocol/recursive_requirement.swift index c99dadbb6b4bd..eab9ac2f8687e 100644 --- a/test/decl/protocol/recursive_requirement.swift +++ b/test/decl/protocol/recursive_requirement.swift @@ -91,7 +91,7 @@ protocol AsExistentialB { } protocol AsExistentialAssocTypeA { - var delegate : AsExistentialAssocTypeB? { get } + var delegate : AsExistentialAssocTypeB? { get } // expected-warning {{protocol 'AsExistentialAssocTypeB' as a type must be explicitly marked as 'any'}} } protocol AsExistentialAssocTypeB { func aMethod(_ object : AsExistentialAssocTypeA) @@ -103,7 +103,7 @@ protocol AsExistentialAssocTypeAgainA { associatedtype Bar } protocol AsExistentialAssocTypeAgainB { - func aMethod(_ object : AsExistentialAssocTypeAgainA) + func aMethod(_ object : AsExistentialAssocTypeAgainA) // expected-warning {{protocol 'AsExistentialAssocTypeAgainA' as a type must be explicitly marked as 'any'}} } // SR-547 diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index e3c3d6a3cb6eb..f11f5ae72b7fc 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -176,7 +176,7 @@ func testOptionalSequence() { } // FIXME: Should this be allowed? -func testExistentialSequence(s: Sequence) { +func testExistentialSequence(s: any Sequence) { for x in s { // expected-error {{protocol 'Sequence' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} _ = x } diff --git a/test/type/protocol_types.swift b/test/type/protocol_types.swift index d2d02dd612a3b..41df887c396d0 100644 --- a/test/type/protocol_types.swift +++ b/test/type/protocol_types.swift @@ -3,7 +3,7 @@ protocol HasSelfRequirements { func foo(_ x: Self) - func returnsOwnProtocol() -> HasSelfRequirements + func returnsOwnProtocol() -> HasSelfRequirements // expected-warning {{protocol 'HasSelfRequirements' as a type must be explicitly marked as 'any'}} } protocol Bar { // init() methods should not prevent use as an existential. @@ -36,10 +36,10 @@ 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(_: HasSelfRequirements, - _: HasSelfRequirements & Bar, - _: Compo, - _: CompoAssocType.Compo) { } +func useAsType(_: any HasSelfRequirements, + _: any HasSelfRequirements & Bar, + _: any Compo, + _: any CompoAssocType.Compo) { } struct TypeRequirement {} struct CompoTypeRequirement {} @@ -74,7 +74,7 @@ do { func checkIt(_ js: Any) throws { switch js { - case let dbl as HasAssoc: + case let dbl as HasAssoc: // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} throw MyError.bad(dbl) default: @@ -83,8 +83,8 @@ do { } } -func testHasAssoc(_ x: Any, _: HasAssoc) { - if let p = x as? HasAssoc { +func testHasAssoc(_ x: Any, _: HasAssoc) { // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} + if let p = x as? any HasAssoc { p.foo() // don't crash here. } @@ -92,18 +92,18 @@ func testHasAssoc(_ x: Any, _: HasAssoc) { typealias Assoc = Int func foo() {} - func method() -> HasAssoc {} + func method() -> HasAssoc {} // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} } } // SR-38 -var b: HasAssoc +var b: HasAssoc // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} // Further generic constraint error testing - typealias used inside statements protocol P {} typealias MoreHasAssoc = HasAssoc & P func testHasMoreAssoc(_ x: Any) { - if let p = x as? MoreHasAssoc { + if let p = x as? any MoreHasAssoc { p.foo() // don't crash here. } } @@ -118,34 +118,34 @@ typealias X = Struct1 _ = Struct1.self typealias AliasWhere = T -where T : HasAssoc, T.Assoc == HasAssoc +where T : HasAssoc, T.Assoc == HasAssoc // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} struct StructWhere where T : HasAssoc, - T.Assoc == HasAssoc {} + T.Assoc == any HasAssoc {} -protocol ProtocolWhere where T == HasAssoc { +protocol ProtocolWhere where T == HasAssoc { // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} associatedtype T associatedtype U : HasAssoc - where U.Assoc == HasAssoc + where U.Assoc == any HasAssoc } -extension HasAssoc where Assoc == HasAssoc {} +extension HasAssoc where Assoc == HasAssoc {} // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} func FunctionWhere(_: T) where T : HasAssoc, - T.Assoc == HasAssoc {} + T.Assoc == any HasAssoc {} struct SubscriptWhere { subscript(_: T) -> Int where T : HasAssoc, - T.Assoc == HasAssoc { + T.Assoc == any HasAssoc { get {} set {} } } struct OuterGeneric { - func contextuallyGenericMethod() where T == HasAssoc {} + func contextuallyGenericMethod() where T == any HasAssoc {} } From 6060de6be98bb0f0721b3c2de7511c8956848afd Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 8 Jan 2022 15:50:25 -0800 Subject: [PATCH 20/24] [AST] Teach ExistentialType::get to only produce ExistentialType when explicit existential types are enabled. --- include/swift/AST/ASTContext.h | 1 - include/swift/AST/Decl.h | 5 ++++ include/swift/AST/Types.h | 2 +- lib/AST/ASTContext.cpp | 41 +++++++++++--------------- lib/AST/Type.cpp | 7 ++--- lib/AST/TypeJoinMeet.cpp | 5 ---- lib/ClangImporter/ImportType.cpp | 27 ++++++----------- lib/SILGen/SILGenType.cpp | 5 +--- lib/Sema/CSSimplify.cpp | 7 +---- lib/Sema/DerivedConformanceCodable.cpp | 10 ++----- lib/Sema/TypeCheckProtocol.cpp | 6 +--- lib/Sema/TypeCheckType.cpp | 11 ++----- 12 files changed, 42 insertions(+), 85 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index d98502304b0c0..bb83a245859b1 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -531,7 +531,6 @@ class ASTContext final { /// Retrieve the declaration of Swift.Error. ProtocolDecl *getErrorDecl() const; - CanType getExceptionType() const; CanType getErrorExistentialType() const; #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ff6fe4e2225b5..8ca2157cda0d3 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4363,6 +4363,11 @@ class ProtocolDecl final : public NominalTypeDecl { /// not exist. AssociatedTypeDecl *getAssociatedType(Identifier name) const; + /// Returns the existential type for this protocol. + Type getExistentialType() const { + return ExistentialType::get(getDeclaredInterfaceType()); + } + /// Walk this protocol and all of the protocols inherited by this protocol, /// transitively, invoking the callback function for each protocol. /// diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 41026850b28a3..a848f1dbcff48 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5248,7 +5248,7 @@ class ExistentialType final : public TypeBase { ConstraintType(constraintType) {} public: - static ExistentialType *get(Type constraint); + static Type get(Type constraint); Type getConstraintType() const { return ConstraintType; } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e77fb51158f8a..e547554e69b9f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -847,9 +847,9 @@ Type ASTContext::get##NAME##Type() const { \ } #include "swift/AST/KnownStdlibTypes.def" -CanType ASTContext::getExceptionType() const { +CanType ASTContext::getErrorExistentialType() const { if (auto exn = getErrorDecl()) { - return exn->getDeclaredInterfaceType()->getCanonicalType(); + return exn->getExistentialType()->getCanonicalType(); } else { // Use Builtin.NativeObject just as a stand-in. return TheNativeObjectType; @@ -860,16 +860,6 @@ ProtocolDecl *ASTContext::getErrorDecl() const { return getProtocol(KnownProtocolKind::Error); } -CanType ASTContext::getErrorExistentialType() const { - Type errorType = getExceptionType(); - if (LangOpts.EnableExplicitExistentialTypes && - errorType->isConstraintType()) { - errorType = ExistentialType::get(errorType); - } - - return errorType->getCanonicalType(); -} - EnumElementDecl *ASTContext::getOptionalSomeDecl() const { if (!getImpl().OptionalSomeDecl) getImpl().OptionalSomeDecl = getOptionalDecl()->getUniqueElement(/*hasVal*/true); @@ -2266,7 +2256,7 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) { auto &entry = selfConformances[protocol]; if (!entry) { entry = new (*this, AllocationArena::Permanent) - SelfProtocolConformance(protocol->getDeclaredInterfaceType()); + SelfProtocolConformance(protocol->getExistentialType()); } return entry; } @@ -3367,15 +3357,7 @@ ExistentialMetatypeType::ExistentialMetatypeType(Type T, } Type ExistentialMetatypeType::getExistentialInstanceType() { - auto instanceType = getInstanceType(); - // Note that Any and AnyObject don't yet use ExistentialType. - if (getASTContext().LangOpts.EnableExplicitExistentialTypes && - !instanceType->is() && - !(instanceType->isAny() || instanceType->isAnyObject())) { - instanceType = ExistentialType::get(instanceType); - } - - return instanceType; + return ExistentialType::get(getInstanceType()); } ModuleType *ModuleType::get(ModuleDecl *M) { @@ -4130,11 +4112,22 @@ ProtocolType::ProtocolType(ProtocolDecl *TheDecl, Type Parent, RecursiveTypeProperties properties) : NominalType(TypeKind::Protocol, &Ctx, TheDecl, Parent, properties) { } -ExistentialType *ExistentialType::get(Type constraint) { +Type ExistentialType::get(Type constraint) { + auto &C = constraint->getASTContext(); + if (!C.LangOpts.EnableExplicitExistentialTypes) + return constraint; + + // FIXME: Any and AnyObject don't yet use ExistentialType. + if (constraint->isAny() || constraint->isAnyObject()) + return constraint; + + // ExistentialMetatypeType is already an existential type. + if (constraint->is()) + return 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; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 84946ea475bb2..e27fa2f517438 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1501,7 +1501,7 @@ CanType TypeBase::computeCanonicalType() { case TypeKind::Existential: { auto *existential = cast(this); auto constraint = existential->getConstraintType()->getCanonicalType(); - Result = ExistentialType::get(constraint); + Result = ExistentialType::get(constraint).getPointer(); break; } case TypeKind::ExistentialMetatype: { @@ -3251,10 +3251,7 @@ Type ArchetypeType::getExistentialType() const { auto constraint = ProtocolCompositionType::get( ctx, constraintTypes, requiresClass()); - if (ctx.LangOpts.EnableExplicitExistentialTypes) - return ExistentialType::get(constraint); - - return constraint; + return ExistentialType::get(constraint); } PrimaryArchetypeType::PrimaryArchetypeType(const ASTContext &Ctx, diff --git a/lib/AST/TypeJoinMeet.cpp b/lib/AST/TypeJoinMeet.cpp index c40e424960014..2c8db30bae1f6 100644 --- a/lib/AST/TypeJoinMeet.cpp +++ b/lib/AST/TypeJoinMeet.cpp @@ -287,11 +287,6 @@ CanType TypeJoin::visitExistentialType(CanType second) { if (!joinInstance) return CanType(); - if (joinInstance->is() || - joinInstance->isAny() || - joinInstance->isAnyObject()) - return joinInstance; - return ExistentialType::get(joinInstance)->getCanonicalType(); } diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index b83f229a4823d..ff8f530bcd18f 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1049,12 +1049,10 @@ namespace { if (memberTypes.empty()) hasExplicitAnyObject = true; - Type importedTypeArg = ProtocolCompositionType::get( - Impl.SwiftContext, memberTypes, - hasExplicitAnyObject); - if (Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) { - importedTypeArg = ExistentialType::get(importedTypeArg); - } + Type importedTypeArg = ExistentialType::get( + ProtocolCompositionType::get( + Impl.SwiftContext, memberTypes, + hasExplicitAnyObject)); importedTypeArgs.push_back(importedTypeArg); } } @@ -1183,8 +1181,7 @@ namespace { } } - if (bridgedType->isConstraintType() && - Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) + if (bridgedType->isConstraintType()) bridgedType = ExistentialType::get(bridgedType); return { importedType, @@ -1207,13 +1204,9 @@ namespace { members.push_back(proto->getDeclaredInterfaceType()); } - importedType = ProtocolCompositionType::get(Impl.SwiftContext, - members, - /*HasExplicitAnyObject=*/false); - - if (Impl.SwiftContext.LangOpts.EnableExplicitExistentialTypes) { - importedType = ExistentialType::get(importedType); - } + importedType = ExistentialType::get( + ProtocolCompositionType::get(Impl.SwiftContext, members, + /*HasExplicitAnyObject=*/false)); } // Class or Class

maps to an existential metatype. @@ -2508,9 +2501,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( bool paramIsIUO; if (kind == SpecialMethodKind::NSDictionarySubscriptGetter && paramTy->isObjCIdType()) { - swiftParamTy = SwiftContext.getNSCopyingType(); - if (SwiftContext.LangOpts.EnableExplicitExistentialTypes) - swiftParamTy = ExistentialType::get(swiftParamTy); + swiftParamTy = ExistentialType::get(SwiftContext.getNSCopyingType()); if (!swiftParamTy) return {Type(), false}; if (optionalityOfParam != OTK_None) diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 412cbdab5749a..20f0383d1cf24 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -842,10 +842,7 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, ProtocolConformanceRef(conformance)); // Open the protocol type. - Type existential = protocolType; - if (SGM.getASTContext().LangOpts.EnableExplicitExistentialTypes) - existential = ExistentialType::get(protocolType); - auto openedType = OpenedArchetypeType::get(existential); + auto openedType = OpenedArchetypeType::get(protocol->getExistentialType()); // Form the substitutions for calling the witness. auto witnessSubs = SubstitutionMap::getProtocolSubstitutions(protocol, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a62ed897ab032..1124286eefd70 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7009,12 +7009,7 @@ static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType, if (!nilLiteral) return false; - auto nilLiteralType = nilLiteral->getDeclaredType(); - if (ctx.LangOpts.EnableExplicitExistentialTypes) { - nilLiteralType = ExistentialType::get(nilLiteralType); - } - - return toType->isEqual(nilLiteralType) && + return toType->isEqual(nilLiteral->getExistentialType()) && fromType->getOptionalObjectType(); } diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 5f37ed3d119da..3273cb60d04da 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -1211,10 +1211,7 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { // output: () // Create from the inside out: - auto encoderType = C.getEncoderType(); - if (C.LangOpts.EnableExplicitExistentialTypes) - encoderType = ExistentialType::get(encoderType); - + auto encoderType = ExistentialType::get(C.getEncoderType()); auto returnType = TupleType::getEmpty(C); // Params: (Encoder) @@ -1805,10 +1802,7 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { // Compute from the inside out: // Params: (Decoder) - auto decoderType = C.getDecoderType(); - if (C.LangOpts.EnableExplicitExistentialTypes) - decoderType = ExistentialType::get(decoderType); - + auto decoderType = ExistentialType::get(C.getDecoderType()); auto *decoderParamDecl = new (C) ParamDecl( SourceLoc(), SourceLoc(), C.Id_from, SourceLoc(), C.Id_decoder, conformanceDC); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index f01d84783cdb6..28ef660793272 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4792,11 +4792,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( // With SE-0335, using a type alias as both a type witness and a generic // constraint will be disallowed in Swift 6, because existential types // must be explicit, and a generic constraint isn't a valid type witness. - // - // Note that Any and AnyObject aren't yet resolved using ExistentialType. - if (getASTContext().LangOpts.EnableExplicitExistentialTypes && - memberType->isConstraintType() && - !(memberType->isAny() || memberType->isAnyObject())) { + if (memberType->isConstraintType()) { memberType = ExistentialType::get(memberType); } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index a42ce813b7f84..b639d46fee7b5 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3449,9 +3449,7 @@ TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, return ErrorType::get(getASTContext()); } - // FIXME: Don't use ExistentialType for AnyObject for now. - if (result->isConstraintType() && !result->isAnyObject() && - getASTContext().LangOpts.EnableExplicitExistentialTypes && + if (result->isConstraintType() && options.isConstraintImplicitExistential()) { return ExistentialType::get(result); } @@ -3840,10 +3838,8 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, auto composition = ProtocolCompositionType::get(getASTContext(), Members, /*HasExplicitAnyObject=*/false); - if (getASTContext().LangOpts.EnableExplicitExistentialTypes && - options.isConstraintImplicitExistential() && - !composition->isAny()) { - composition = ExistentialType::get(composition); + if (options.isConstraintImplicitExistential()) { + return ExistentialType::get(composition); } return composition; } @@ -3871,7 +3867,6 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, diagnose(repr->getLoc(), diag::unnecessary_any, constraintType) .fixItRemove({anyStart, anyEnd}); - return constraintType; } return ExistentialType::get(constraintType); From c48b5939b745cc75580bd8a421d5818cdf11de1e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 8 Jan 2022 15:55:06 -0800 Subject: [PATCH 21/24] [Diagnostics] Remove the warning for 'any Any' and 'any AnyObject'. --- include/swift/AST/DiagnosticsSema.def | 2 -- lib/Sema/TypeCheckType.cpp | 7 ------- test/type/explicit_existential.swift | 6 +++--- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d462311ac6b8f..4987a8a41fa7f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4648,8 +4648,6 @@ 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)) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b639d46fee7b5..d96094b47393f 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3862,13 +3862,6 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr, 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); } diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index af27c20f4890d..2b8999eea8896 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -149,9 +149,9 @@ func testInvalidAny() { 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'}} +func anyAny() { + let _: any Any + let _: any AnyObject } protocol P1 {} From 992a871a73b8adc3299d342629344a33ca0208e1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 10 Jan 2022 23:42:25 -0800 Subject: [PATCH 22/24] [Type Reconstruction] Re-construct existential types with ExistentialType by propagating whether the decoded type is for a requirement through TypeDecoder. --- include/swift/AST/ASTDemangler.h | 5 +- include/swift/Demangling/TypeDecoder.h | 60 ++++++++++++++------- include/swift/Reflection/TypeRefBuilder.h | 5 +- lib/AST/ASTDemangler.cpp | 20 ++++--- lib/IRGen/IRGenDebugInfo.cpp | 25 ++++++--- stdlib/public/Reflection/TypeRefBuilder.cpp | 6 ++- stdlib/public/runtime/MetadataLookup.cpp | 9 ++-- 7 files changed, 83 insertions(+), 47 deletions(-) diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index 76c90f017c9a5..8e4100d77396e 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -66,7 +66,7 @@ class ASTBuilder { Demangle::NodeFactory &getNodeFactory() { return Factory; } - Type decodeMangledType(NodePointer node); + Type decodeMangledType(NodePointer node, bool forRequirement = true); Type createBuiltinType(StringRef builtinName, StringRef mangledName); TypeDecl *createTypeDecl(NodePointer node); @@ -109,7 +109,8 @@ class ASTBuilder { Type createProtocolCompositionType(ArrayRef protocols, Type superclass, - bool isClassBound); + bool isClassBound, + bool forRequirement = true); Type createExistentialMetatypeType(Type instance, Optional repr=None); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 73a86f320d77c..07b5e5161dc70 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -372,12 +372,16 @@ void decodeRequirement(NodePointer node, BuiltType constraintType; if (child->getKind() == - Demangle::Node::Kind::DependentGenericConformanceRequirement || - child->getKind() == - Demangle::Node::Kind::DependentGenericSameTypeRequirement) { + Demangle::Node::Kind::DependentGenericConformanceRequirement) { constraintType = Builder.decodeMangledType(child->getChild(1)); if (!constraintType) return; + } else if (child->getKind() == + Demangle::Node::Kind::DependentGenericSameTypeRequirement) { + constraintType = Builder.decodeMangledType( + child->getChild(1), /*forRequirement=*/false); + if (!constraintType) + return; } switch (child->getKind()) { @@ -468,15 +472,17 @@ class TypeDecoder { explicit TypeDecoder(BuilderType &Builder) : Builder(Builder) {} /// Given a demangle tree, attempt to turn it into a type. - TypeLookupErrorOr decodeMangledType(NodePointer Node) { - return decodeMangledType(Node, 0); + TypeLookupErrorOr decodeMangledType(NodePointer Node, + bool forRequirement = true) { + return decodeMangledType(Node, 0, forRequirement); } protected: static const unsigned MaxDepth = 1024; TypeLookupErrorOr decodeMangledType(NodePointer Node, - unsigned depth) { + unsigned depth, + bool forRequirement = true) { if (depth > TypeDecoder::MaxDepth) return TypeLookupError("Mangled type is too complex"); @@ -499,7 +505,8 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children."); - return decodeMangledType(Node->getChild(0), depth + 1); + return decodeMangledType(Node->getChild(0), depth + 1, + forRequirement); case NodeKind::Class: { #if SWIFT_OBJC_INTEROP @@ -543,7 +550,8 @@ class TypeDecoder { return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList"); for (auto genericArg : *genericArgs) { - auto paramType = decodeMangledType(genericArg, depth + 1); + auto paramType = decodeMangledType(genericArg, depth + 1, + /*forRequirement=*/false); if (paramType.isError()) return paramType; args.push_back(paramType.getType()); @@ -698,14 +706,16 @@ class TypeDecoder { } return Builder.createProtocolCompositionType(Protocols, Superclass, - IsClassBound); + IsClassBound, + forRequirement); } case NodeKind::Protocol: case NodeKind::ProtocolSymbolicReference: { if (auto Proto = decodeMangledProtocolType(Node, depth + 1)) { return Builder.createProtocolCompositionType(Proto, BuiltType(), - /*IsClassBound=*/false); + /*IsClassBound=*/false, + forRequirement); } return MAKE_NODE_TYPE_ERROR0(Node, "failed to decode protocol type"); @@ -845,7 +855,8 @@ class TypeDecoder { Node->getKind() == NodeKind::EscapingObjCBlock); auto result = - decodeMangledType(Node->getChild(firstChildIdx + 1), depth + 1); + decodeMangledType(Node->getChild(firstChildIdx + 1), depth + 1, + /*forRequirement=*/false); if (result.isError()) return result; return Builder.createFunctionType( @@ -962,7 +973,8 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - return decodeMangledType(Node->getChild(0), depth + 1); + return decodeMangledType(Node->getChild(0), depth + 1, + /*forRequirement=*/false); case NodeKind::Tuple: { llvm::SmallVector elements; @@ -994,7 +1006,8 @@ class TypeDecoder { // Decode the element type. auto elementType = - decodeMangledType(element->getChild(typeChildIndex), depth + 1); + decodeMangledType(element->getChild(typeChildIndex), depth + 1, + /*forRequirement=*/false); if (elementType.isError()) return elementType; @@ -1012,9 +1025,11 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - return decodeMangledType(Node->getChild(1), depth + 1); + return decodeMangledType(Node->getChild(1), depth + 1, + /*forRequirement=*/false); } - return decodeMangledType(Node->getChild(0), depth + 1); + return decodeMangledType(Node->getChild(0), depth + 1, + /*forRequirement=*/false); case NodeKind::DependentGenericType: { if (Node->getNumChildren() < 2) @@ -1161,7 +1176,8 @@ return {}; // Not Implemented! "more substitutions than generic params"); while (index >= genericParamsAtDepth[paramDepth]) ++paramDepth, index = 0; - auto substTy = decodeMangledType(subst, depth + 1); + auto substTy = decodeMangledType(subst, depth + 1, + /*forRequirement=*/false); if (substTy.isError()) return substTy; substitutions.emplace_back( @@ -1243,7 +1259,8 @@ return {}; // Not Implemented! if (genericsNode->getKind() != NodeKind::TypeList) break; for (auto argNode : *genericsNode) { - auto arg = decodeMangledType(argNode, depth + 1); + auto arg = decodeMangledType(argNode, depth + 1, + /*forRequirement=*/false); if (arg.isError()) return arg; genericArgsBuf.push_back(arg.getType()); @@ -1480,7 +1497,8 @@ return {}; // Not Implemented! } } - auto paramType = decodeMangledType(node, depth + 1); + auto paramType = decodeMangledType(node, depth + 1, + /*forRequirement=*/false); if (paramType.isError()) return false; @@ -1544,8 +1562,10 @@ return {}; // Not Implemented! template inline TypeLookupErrorOr -decodeMangledType(BuilderType &Builder, NodePointer Node) { - return TypeDecoder(Builder).decodeMangledType(Node); +decodeMangledType(BuilderType &Builder, NodePointer Node, + bool forRequirement = false) { + return TypeDecoder(Builder) + .decodeMangledType(Node, forRequirement); } SWIFT_END_INLINE_NAMESPACE diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 913508ce5a03d..8732961f7cc30 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -289,7 +289,7 @@ class TypeRefBuilder { void clearNodeFactory() { Dem.clear(); } - BuiltType decodeMangledType(Node *node); + BuiltType decodeMangledType(Node *node, bool forRequirement = true); /// /// Factory methods for all TypeRef kinds @@ -492,7 +492,8 @@ class TypeRefBuilder { const ProtocolCompositionTypeRef * createProtocolCompositionType(llvm::ArrayRef protocols, - BuiltType superclass, bool isClassBound) { + BuiltType superclass, bool isClassBound, + bool forRequirement = true) { std::vector protocolRefs; for (const auto &protocol : protocols) { if (!protocol) diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 6e2266b7938ad..149474bd664eb 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -70,8 +70,9 @@ TypeDecl *swift::Demangle::getTypeDeclForUSR(ASTContext &ctx, return getTypeDeclForMangling(ctx, mangling); } -Type ASTBuilder::decodeMangledType(NodePointer node) { - return swift::Demangle::decodeMangledType(*this, node).getType(); +Type ASTBuilder::decodeMangledType(NodePointer node, bool forRequirement) { + return swift::Demangle::decodeMangledType(*this, node, forRequirement) + .getType(); } TypeDecl *ASTBuilder::createTypeDecl(NodePointer node) { @@ -578,22 +579,19 @@ Type ASTBuilder::createImplFunctionType( Type ASTBuilder::createProtocolCompositionType( ArrayRef protocols, Type superclass, - bool isClassBound) { + bool isClassBound, + bool forRequirement) { std::vector members; for (auto protocol : protocols) members.push_back(protocol->getDeclaredInterfaceType()); if (superclass && superclass->getClassOrBoundGenericClass()) members.push_back(superclass); - // FIXME: When explicit existential types are enabled, protocol - // compositions should be wrapped in ExistentialType based on - // context, similar to how protocol compositions are resolved - // during type resolution. For example, protocol compositions - // in parameter types should be wrapped in ExistentialType, but - // protocol compositions on the right side of a conformance - // requirement should not. Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound); - return composition; + if (forRequirement) + return composition; + + return ExistentialType::get(composition); } static MetatypeRepresentation diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index b0fc35be773e2..fb64cc9ee854f 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -97,6 +97,22 @@ class EqualUpToClangTypes }; }; +static bool equalWithoutExistentialTypes(Type t1, Type t2) { + if (!t1->getASTContext().LangOpts.EnableExplicitExistentialTypes) + return false; + + auto withoutExistentialTypes = [](Type type) -> Type { + return type.transform([](Type type) -> Type { + if (auto existential = type->getAs()) + return existential->getConstraintType(); + return type; + }); + }; + + return withoutExistentialTypes(t1) + ->isEqual(withoutExistentialTypes(t2)); +} + class IRGenDebugInfoImpl : public IRGenDebugInfo { friend class IRGenDebugInfoImpl; const IRGenOptions &Opts; @@ -900,14 +916,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { Ty->dump(llvm::errs()); abort(); } else if (!Reconstructed->isEqual(Ty) && - // FIXME: Existential types are reconstructed without + // FIXME: Some existential types are reconstructed without // an explicit ExistentialType wrapping the constraint. - !(Ty->getASTContext().LangOpts.EnableExplicitExistentialTypes && - Ty.transform([](Type type) -> Type { - if (auto existential = type->getAs()) - return existential->getConstraintType(); - return type; - })->isEqual(Reconstructed)) && + !equalWithoutExistentialTypes(Reconstructed, Ty) && !EqualUpToClangTypes().check(Reconstructed, Ty)) { // [FIXME: Include-Clang-type-in-mangling] Remove second check llvm::errs() << "Incorrect reconstructed type for " << Result << "\n"; diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index a85eab6dfb3b1..d7a88e95136a3 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -30,8 +30,10 @@ using namespace swift; using namespace reflection; -TypeRefBuilder::BuiltType TypeRefBuilder::decodeMangledType(Node *node) { - return swift::Demangle::decodeMangledType(*this, node).getType(); +TypeRefBuilder::BuiltType +TypeRefBuilder::decodeMangledType(Node *node, bool forRequirement) { + return swift::Demangle::decodeMangledType(*this, node, forRequirement) + .getType(); } RemoteRef TypeRefBuilder::readTypeRef(uint64_t remoteAddr) { diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index ebcfe07948695..d0748a1a3aefb 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -1314,8 +1314,10 @@ class DecodedMetadataBuilder { using BuiltTypeDecl = const ContextDescriptor *; using BuiltProtocolDecl = ProtocolDescriptorRef; - BuiltType decodeMangledType(NodePointer node) { - return Demangle::decodeMangledType(*this, node).getType(); + BuiltType decodeMangledType(NodePointer node, + bool forRequirement = true) { + return Demangle::decodeMangledType(*this, node, forRequirement) + .getType(); } Demangle::NodeFactory &getNodeFactory() { return demangler; } @@ -1483,7 +1485,8 @@ class DecodedMetadataBuilder { TypeLookupErrorOr createProtocolCompositionType(llvm::ArrayRef protocols, - BuiltType superclass, bool isClassBound) const { + BuiltType superclass, bool isClassBound, + bool forRequirement = true) const { // Determine whether we have a class bound. ProtocolClassConstraint classConstraint = ProtocolClassConstraint::Any; if (isClassBound || superclass) { From 6608bf898b17e5fc923a9e32853994c03ea4060e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 13 Jan 2022 19:30:05 -0800 Subject: [PATCH 23/24] [Sema] Fix the source compatibility check for member operators with existential arguments to not return before checking the second argument type. --- lib/Sema/TypeCheckDecl.cpp | 3 ++- test/type/explicit_existential.swift | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c5432dd5cd72a..29afff0f8897a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1903,7 +1903,8 @@ bool swift::isMemberOperator(FuncDecl *decl, Type type) { // accepts member operators on protocols with existential // type arguments. We should consider banning this in Swift 6. if (auto existential = paramType->getAs()) { - return selfNominal == existential->getConstraintType()->getAnyNominal(); + if (selfNominal == existential->getConstraintType()->getAnyNominal()) + return true; } // For a protocol, is it the 'Self' type parameter? diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index 2b8999eea8896..782027217e0e1 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -185,3 +185,11 @@ enum E2: RawRepresentable { return ConcreteComposition() } } + +public protocol MyError {} + +extension MyError { + static func ~=(lhs: any Error, rhs: Self) -> Bool { + return true + } +} From 626bea2bc683f83abffa510a3959ef62c538f755 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 13 Jan 2022 22:31:55 -0800 Subject: [PATCH 24/24] [ConstraintSystem] Return an existential type in getTypeOfMemberReference when the member is a typealias to a protocol or composition. --- lib/Sema/ConstraintSystem.cpp | 7 +++++++ test/type/explicit_existential.swift | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1f6bbe825db30..61042497b768b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1767,6 +1767,13 @@ ConstraintSystem::getTypeOfMemberReference( auto memberTy = TypeChecker::substMemberTypeWithBase(DC->getParentModule(), typeDecl, baseObjTy); + // If the member type is a constraint, e.g. because the + // reference is to a typealias with an underlying protocol + // or composition type, the member reference has existential + // type. + if (memberTy->isConstraintType()) + memberTy = ExistentialType::get(memberTy); + checkNestedTypeConstraints(*this, memberTy, locator); // Convert any placeholders and open any generics. diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index 782027217e0e1..38714635d6a86 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -193,3 +193,12 @@ extension MyError { return true } } + +struct Wrapper { + typealias E = Error +} + +func typealiasMemberReferences(metatype: Wrapper.Type) { + let _: Wrapper.E.Protocol = metatype.E.self + let _: (any Wrapper.E).Type = metatype.E.self +}