Skip to content

Commit 4146a7f

Browse files
authored
Merge pull request #83736 from slavapestov/fix-rdar157329046
Sema: New opaque return type circularity check that doesn't trigger lazy type checking of bodies
2 parents 5857afe + e26034b commit 4146a7f

File tree

9 files changed

+102
-103
lines changed

9 files changed

+102
-103
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3659,7 +3659,8 @@ class OpaqueTypeDecl final :
36593659

36603660
/// The substitutions that map the generic parameters of the opaque type to
36613661
/// the unique underlying types, when that information is known.
3662-
std::optional<SubstitutionMap> getUniqueUnderlyingTypeSubstitutions() const;
3662+
std::optional<SubstitutionMap> getUniqueUnderlyingTypeSubstitutions(
3663+
bool typeCheckFunctionBodies=true) const;
36633664

36643665
void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) {
36653666
assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");

include/swift/AST/Types.h

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7081,15 +7081,20 @@ enum class OpaqueSubstitutionKind {
70817081
/// to their underlying types.
70827082
class ReplaceOpaqueTypesWithUnderlyingTypes {
70837083
private:
7084-
ResilienceExpansion contextExpansion;
7085-
llvm::PointerIntPair<const DeclContext *, 1, bool> inContextAndIsWholeModule;
7084+
const DeclContext *inContext;
7085+
unsigned contextExpansion : 1;
7086+
bool isWholeModule : 1;
7087+
bool typeCheckFunctionBodies : 1;
70867088

70877089
public:
70887090
ReplaceOpaqueTypesWithUnderlyingTypes(const DeclContext *inContext,
70897091
ResilienceExpansion contextExpansion,
7090-
bool isWholeModuleContext)
7091-
: contextExpansion(contextExpansion),
7092-
inContextAndIsWholeModule(inContext, isWholeModuleContext) {}
7092+
bool isWholeModule,
7093+
bool typeCheckFunctionBodies=true)
7094+
: inContext(inContext),
7095+
contextExpansion(unsigned(contextExpansion)),
7096+
isWholeModule(isWholeModule),
7097+
typeCheckFunctionBodies(typeCheckFunctionBodies) {}
70937098

70947099
/// TypeSubstitutionFn
70957100
Type operator()(SubstitutableType *maybeOpaqueType) const;
@@ -7105,13 +7110,6 @@ class ReplaceOpaqueTypesWithUnderlyingTypes {
71057110
static OpaqueSubstitutionKind
71067111
shouldPerformSubstitution(OpaqueTypeDecl *opaque, ModuleDecl *contextModule,
71077112
ResilienceExpansion contextExpansion);
7108-
7109-
private:
7110-
const DeclContext *getContext() const {
7111-
return inContextAndIsWholeModule.getPointer();
7112-
}
7113-
7114-
bool isWholeModule() const { return inContextAndIsWholeModule.getInt(); }
71157113
};
71167114

71177115
/// A function object that can be used as a \c TypeSubstitutionFn and

lib/AST/Decl.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10840,7 +10840,11 @@ bool OpaqueTypeDecl::exportUnderlyingType() const {
1084010840
}
1084110841

1084210842
std::optional<SubstitutionMap>
10843-
OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions() const {
10843+
OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions(
10844+
bool typeCheckFunctionBodies) const {
10845+
if (!typeCheckFunctionBodies)
10846+
return UniqueUnderlyingType;
10847+
1084410848
return evaluateOrDefault(getASTContext().evaluator,
1084510849
UniqueUnderlyingTypeSubstitutionsRequest{this}, {});
1084610850
}

lib/AST/TypeSubstitution.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,10 @@ Type TypeBase::adjustSuperclassMemberDeclType(const ValueDecl *baseDecl,
903903
OpaqueSubstitutionKind
904904
ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
905905
OpaqueTypeDecl *opaque) const {
906-
const auto *inContext = getContext();
907906
auto inModule = inContext ? inContext->getParentModule()
908907
: opaque->getParentModule();
909-
return shouldPerformSubstitution(opaque, inModule, contextExpansion);
908+
return shouldPerformSubstitution(
909+
opaque, inModule, ResilienceExpansion(contextExpansion));
910910
}
911911
OpaqueSubstitutionKind
912912
ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
@@ -1025,7 +1025,7 @@ operator()(SubstitutableType *maybeOpaqueType) const {
10251025
return maybeOpaqueType;
10261026
}
10271027

1028-
auto subs = decl->getUniqueUnderlyingTypeSubstitutions();
1028+
auto subs = decl->getUniqueUnderlyingTypeSubstitutions(typeCheckFunctionBodies);
10291029
// If the body of the opaque decl providing decl has not been type checked we
10301030
// don't have a underlying substitution.
10311031
if (!subs.has_value())
@@ -1038,16 +1038,12 @@ operator()(SubstitutableType *maybeOpaqueType) const {
10381038

10391039
// Check that we are allowed to substitute the underlying type into the
10401040
// context.
1041-
auto inContext = this->getContext();
1042-
auto isContextWholeModule = this->isWholeModule();
1043-
auto contextExpansion = this->contextExpansion;
10441041
if (inContext &&
10451042
partialSubstTy.findIf(
1046-
[inContext, substitutionKind, isContextWholeModule,
1047-
contextExpansion](Type t) -> bool {
1043+
[&](Type t) -> bool {
10481044
if (!canSubstituteTypeInto(t, inContext, substitutionKind,
1049-
contextExpansion,
1050-
isContextWholeModule))
1045+
ResilienceExpansion(contextExpansion),
1046+
isWholeModule))
10511047
return true;
10521048
return false;
10531049
}))
@@ -1154,16 +1150,12 @@ operator()(InFlightSubstitution &IFS, Type maybeOpaqueType,
11541150

11551151
// Check that we are allowed to substitute the underlying type into the
11561152
// context.
1157-
auto inContext = this->getContext();
1158-
auto isContextWholeModule = this->isWholeModule();
1159-
auto contextExpansion = this->contextExpansion;
11601153
if (inContext &&
11611154
partialSubstTy.findIf(
1162-
[inContext, substitutionKind, isContextWholeModule,
1163-
contextExpansion](Type t) -> bool {
1155+
[&](Type t) -> bool {
11641156
if (!canSubstituteTypeInto(t, inContext, substitutionKind,
1165-
contextExpansion,
1166-
isContextWholeModule))
1157+
ResilienceExpansion(contextExpansion),
1158+
isWholeModule))
11671159
return true;
11681160
return false;
11691161
})) {

lib/Sema/MiscDiagnostics.cpp

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
#include "swift/AST/DiagnosticsSema.h"
2828
#include "swift/AST/ExistentialLayout.h"
2929
#include "swift/AST/Expr.h"
30-
#include "swift/AST/InFlightSubstitution.h"
3130
#include "swift/AST/NameLookup.h"
3231
#include "swift/AST/NameLookupRequests.h"
3332
#include "swift/AST/Pattern.h"
@@ -3677,50 +3676,6 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
36773676
}
36783677
}
36793678

3680-
bool isSelfReferencing(const Candidate &candidate) {
3681-
auto substitutions = std::get<1>(candidate);
3682-
3683-
// The underlying type can't be defined recursively
3684-
// in terms of the opaque type itself.
3685-
for (auto genericParam : OpaqueDecl->getOpaqueGenericParams()) {
3686-
auto underlyingType = Type(genericParam).subst(substitutions);
3687-
3688-
// Look through underlying types of other opaque archetypes known to
3689-
// us. This is not something the type checker is allowed to do in
3690-
// general, since the intent is that the underlying type is completely
3691-
// hidden from view at the type system level. However, here we're
3692-
// trying to catch recursive underlying types before we proceed to
3693-
// SIL, so we specifically want to erase opaque archetypes just
3694-
// for the purpose of this check.
3695-
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
3696-
OpaqueDecl->getDeclContext(),
3697-
ResilienceExpansion::Maximal,
3698-
/*isWholeModuleContext=*/false);
3699-
InFlightSubstitution IFS(replacer, replacer,
3700-
SubstFlags::SubstituteOpaqueArchetypes |
3701-
SubstFlags::PreservePackExpansionLevel);
3702-
auto simplifiedUnderlyingType = underlyingType.subst(IFS);
3703-
3704-
auto isSelfReferencing =
3705-
(IFS.wasLimitReached() ||
3706-
simplifiedUnderlyingType.findIf([&](Type t) -> bool {
3707-
if (auto *other = t->getAs<OpaqueTypeArchetypeType>()) {
3708-
return other->getDecl() == OpaqueDecl;
3709-
}
3710-
return false;
3711-
}));
3712-
3713-
if (isSelfReferencing) {
3714-
Ctx.Diags.diagnose(std::get<0>(candidate)->getLoc(),
3715-
diag::opaque_type_self_referential_underlying_type,
3716-
underlyingType);
3717-
return true;
3718-
}
3719-
}
3720-
3721-
return false;
3722-
}
3723-
37243679
// A single unique underlying substitution.
37253680
void finalizeUnique(const Candidate &candidate) {
37263681
// If we have one successful candidate, then save it as the underlying
@@ -3819,11 +3774,6 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
38193774
auto candidate =
38203775
std::make_tuple(underlyingToOpaque->getSubExpr(), subMap, isUnique);
38213776

3822-
if (isSelfReferencing(candidate)) {
3823-
HasInvalidReturn = true;
3824-
return Action::Stop();
3825-
}
3826-
38273777
if (subMap.getRecursiveProperties().hasDynamicSelf()) {
38283778
Ctx.Diags.diagnose(E->getLoc(),
38293779
diag::opaque_type_cannot_contain_dynamic_self);

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/ASTWalker.h"
2222
#include "swift/AST/DiagnosticsSema.h"
2323
#include "swift/AST/ExistentialLayout.h"
24+
#include "swift/AST/InFlightSubstitution.h"
2425
#include "swift/AST/GenericEnvironment.h"
2526
#include "swift/AST/ParameterList.h"
2627
#include "swift/AST/ProtocolConformance.h"
@@ -267,6 +268,53 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
267268
return opaqueDecl;
268269
}
269270

271+
void TypeChecker::checkCircularOpaqueReturnTypeDecl(OpaqueTypeDecl *opaqueDecl) {
272+
auto optSubs = opaqueDecl->getUniqueUnderlyingTypeSubstitutions(
273+
/*typeCheckFunctionBodies=*/false);
274+
if (!optSubs)
275+
return;
276+
277+
auto substitutions = *optSubs;
278+
279+
// The underlying type can't be defined recursively
280+
// in terms of the opaque type itself.
281+
for (auto genericParam : opaqueDecl->getOpaqueGenericParams()) {
282+
auto underlyingType = Type(genericParam).subst(substitutions);
283+
284+
// Look through underlying types of other opaque archetypes known to
285+
// us. This is not something the type checker is allowed to do in
286+
// general, since the intent is that the underlying type is completely
287+
// hidden from view at the type system level. However, here we're
288+
// trying to catch recursive underlying types before we proceed to
289+
// SIL, so we specifically want to erase opaque archetypes just
290+
// for the purpose of this check.
291+
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
292+
opaqueDecl->getDeclContext(),
293+
ResilienceExpansion::Maximal,
294+
/*isWholeModuleContext=*/false,
295+
/*typeCheckFunctionBodies=*/false);
296+
InFlightSubstitution IFS(replacer, replacer,
297+
SubstFlags::SubstituteOpaqueArchetypes |
298+
SubstFlags::PreservePackExpansionLevel);
299+
auto simplifiedUnderlyingType = underlyingType.subst(IFS);
300+
301+
auto isSelfReferencing =
302+
(IFS.wasLimitReached() ||
303+
simplifiedUnderlyingType.findIf([&](Type t) -> bool {
304+
if (auto *other = t->getAs<OpaqueTypeArchetypeType>()) {
305+
return other->getDecl() == opaqueDecl;
306+
}
307+
return false;
308+
}));
309+
310+
if (isSelfReferencing) {
311+
opaqueDecl->getNamingDecl()->diagnose(
312+
diag::opaque_type_self_referential_underlying_type,
313+
underlyingType);
314+
}
315+
}
316+
}
317+
270318
static bool checkProtocolSelfRequirementsImpl(
271319
ASTContext &ctx, ProtocolDecl *proto, ValueDecl *decl,
272320
GenericSignature originalSig,

lib/Sema/TypeChecker.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ TypeCheckPrimaryFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
326326
}
327327
}
328328
SF->typeCheckDelayedFunctions();
329+
330+
for (auto *opaqueDecl : SF->getOpaqueReturnTypeDecls()) {
331+
TypeChecker::checkCircularOpaqueReturnTypeDecl(opaqueDecl);
332+
}
329333
}
330334

331335
// If region-based isolation is enabled, we diagnose unnecessary

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ void typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD);
506506

507507
void typeCheckDecl(Decl *D);
508508

509+
void checkCircularOpaqueReturnTypeDecl(OpaqueTypeDecl *opaqueDecl);
510+
509511
void addImplicitDynamicAttribute(Decl *D);
510512
void checkDeclAttributes(Decl *D);
511513
void checkDeclABIAttribute(Decl *apiDecl, ABIAttr *abiAttr);

test/Generics/infinite_opaque_result_type.swift

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@ func concrete1() -> some Any {
55
return concrete1()
66
}
77

8-
func concrete2() -> some Any {
9-
return [concrete2()] // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
8+
func concrete2() -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
9+
return [concrete2()]
1010
}
1111

1212

13-
func concrete1a() -> some Any {
14-
return concrete1b() // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
13+
func concrete1a() -> some Any { // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
14+
return concrete1b()
1515
}
1616

17-
func concrete1b() -> some Any {
17+
func concrete1b() -> some Any { // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
1818
return concrete1a()
1919
}
2020

2121

22-
func concrete2a() -> some Any {
23-
return [concrete2b()] // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
22+
func concrete2a() -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
23+
return [concrete2b()]
2424
}
2525

26-
func concrete2b() -> some Any {
26+
func concrete2b() -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
2727
return [concrete2a()]
2828
}
2929

@@ -33,41 +33,41 @@ func generic1<T>(_ t: T) -> some Any {
3333
return generic1(t)
3434
}
3535

36-
func generic2<T>(_ t: T) -> some Any {
37-
return [generic2(t)] // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
36+
func generic2<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
37+
return [generic2(t)]
3838
}
3939

4040

41-
func generic1a<T>(_ t: T) -> some Any {
42-
return generic1b(t) // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
41+
func generic1a<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
42+
return generic1b(t)
4343
}
4444

45-
func generic1b<T>(_ t: T) -> some Any {
45+
func generic1b<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as 'some Any', which defines the opaque type in terms of itself}}
4646
return generic1a(t)
4747
}
4848

4949

50-
func generic2a<T>(_ t: T) -> some Any {
51-
return [generic2b(t)] // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
50+
func generic2a<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
51+
return [generic2b(t)]
5252
}
5353

54-
func generic2b<T>(_ t: T) -> some Any {
54+
func generic2b<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
5555
return [generic2a(t)]
5656
}
5757

5858

59-
func generic3a<T>(_ t: T) -> some Any {
60-
return [generic3b(t)] // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
59+
func generic3a<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
60+
return [generic3b(t)]
6161
}
6262

63-
func generic3b<T>(_ t: T) -> some Any {
63+
func generic3b<T>(_ t: T) -> some Any { // expected-error {{function opaque return type was inferred as '[some Any]', which defines the opaque type in terms of itself}}
6464
return [generic3a([t])]
6565
}
6666

67-
func very_wide1() -> some Any {
68-
return (very_wide2(), very_wide2()) // expected-error {{function opaque return type was inferred as '(some Any, some Any)', which defines the opaque type in terms of itself}}
67+
func very_wide1() -> some Any { // expected-error {{function opaque return type was inferred as '(some Any, some Any)', which defines the opaque type in terms of itself}}
68+
return (very_wide2(), very_wide2())
6969
}
7070

71-
func very_wide2() -> some Any {
71+
func very_wide2() -> some Any { // expected-error {{function opaque return type was inferred as '(some Any, some Any)', which defines the opaque type in terms of itself}}
7272
return (very_wide1(), very_wide1())
7373
}

0 commit comments

Comments
 (0)