Skip to content

Commit 791c9c7

Browse files
committed
Implement most of the semantic analysis for named opaque result types.
Address small gaps in several places to make named opaque result types partially work: * Augment name lookup to look into the generic parameters when inside the result type, which is used both to create structure and add requirements via a `where` clause. * Resolve opaque generic type parameter references to OpaqueTypeArchetypeType instances, as we do for the "some" types * Customize some opaque-type-specific diagnostics and type printing to refer to the opaque generic parameter names specifically * Fix some minor issues with the constraint system not finding already-opened opaque generic type parameters and with the handling of the opaque result type candidate set. The major limitation on opaque types, where we cannot add requirements that aren't strictly protocol or superclass requirements on the generic parameters, remains. Until then, named opaque result types are no more expressive than structural opaque result types.
1 parent 43324e8 commit 791c9c7

File tree

9 files changed

+175
-27
lines changed

9 files changed

+175
-27
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,6 +2779,16 @@ class OpaqueTypeDecl final :
27792779
return OpaqueInterfaceGenericSignature.getInnermostGenericParams();
27802780
}
27812781

2782+
/// Whether the generic parameters of this opaque type declaration were
2783+
/// explicit, i.e., for named opaque result types.
2784+
bool hasExplicitGenericParams() const;
2785+
2786+
/// When the generic parameters were explicit, returns the generic parameter
2787+
/// corresponding to the given ordinal.
2788+
///
2789+
/// Otherwise, returns \c nullptr.
2790+
GenericTypeParamDecl *getExplicitGenericParam(unsigned ordinal) const;
2791+
27822792
/// Retrieve the buffer containing the opaque return type
27832793
/// representations that correspond to the opaque generic parameters.
27842794
ArrayRef<OpaqueReturnTypeRepr *> getOpaqueReturnTypeReprs() const {

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4134,6 +4134,9 @@ ERROR(opaque_type_no_underlying_type_candidates,none,
41344134
ERROR(opaque_type_mismatched_underlying_type_candidates,none,
41354135
"function declares an opaque return type %0, but the return statements "
41364136
"in its body do not have matching underlying types", (TypeRepr *))
4137+
ERROR(opaque_type_mismatched_underlying_type_candidates_named,none,
4138+
"function declares an opaque return type %0, but the return statements "
4139+
"in its body do not have matching underlying types", (Identifier))
41374140
NOTE(opaque_type_underlying_type_candidate_here,none,
41384141
"return statement has underlying type %0", (Type))
41394142
ERROR(opaque_type_self_referential_underlying_type,none,

lib/AST/ASTPrinter.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4338,9 +4338,10 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
43384338
case PrintOptions::OpaqueReturnTypePrintingMode::Description:
43394339
return true;
43404340
case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword:
4341-
return false;
4341+
return opaque->getDecl()->hasExplicitGenericParams();
43424342
case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword:
4343-
return isSimpleUnderPrintOptions(opaque->getExistentialType());
4343+
return opaque->getDecl()->hasExplicitGenericParams() ||
4344+
isSimpleUnderPrintOptions(opaque->getExistentialType());
43444345
}
43454346
llvm_unreachable("bad opaque-return-type printing mode");
43464347
}
@@ -5410,11 +5411,28 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
54105411
}
54115412

54125413
void visitOpaqueTypeArchetypeType(OpaqueTypeArchetypeType *T) {
5414+
// Try to print a named opaque type.
5415+
auto printNamedOpaque = [&] {
5416+
if (auto genericParam =
5417+
T->getDecl()->getExplicitGenericParam(T->getOrdinal())) {
5418+
visit(genericParam->getDeclaredInterfaceType());
5419+
return true;
5420+
}
5421+
5422+
return false;
5423+
};
5424+
54135425
switch (Options.OpaqueReturnTypePrinting) {
54145426
case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword:
5427+
if (printNamedOpaque())
5428+
return;
5429+
54155430
Printer.printKeyword("some", Options, /*Suffix=*/" ");
54165431
LLVM_FALLTHROUGH;
54175432
case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: {
5433+
if (printNamedOpaque())
5434+
return;
5435+
54185436
visit(T->getExistentialType());
54195437
return;
54205438
}

lib/AST/ASTScopeCreation.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,13 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
859859
ASTScopeImpl *leaf = this;
860860

861861
if (!isa<AccessorDecl>(decl)) {
862+
if (auto opaqueRepr = decl->getOpaqueResultTypeRepr()) {
863+
if (auto namedOpaque = dyn_cast<NamedOpaqueReturnTypeRepr>(opaqueRepr)) {
864+
leaf = scopeCreator.addNestedGenericParamScopesToTree(
865+
decl, namedOpaque->getGenericParams(), leaf);
866+
}
867+
}
868+
862869
leaf = scopeCreator.addNestedGenericParamScopesToTree(
863870
decl, decl->getGenericParams(), leaf);
864871

lib/AST/Decl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7829,6 +7829,19 @@ bool OpaqueTypeDecl::isOpaqueReturnTypeOfFunction(
78297829
return false;
78307830
}
78317831

7832+
bool OpaqueTypeDecl::hasExplicitGenericParams() const {
7833+
return getExplicitGenericParam(0) != nullptr;
7834+
}
7835+
7836+
GenericTypeParamDecl *OpaqueTypeDecl::getExplicitGenericParam(
7837+
unsigned ordinal) const {
7838+
if (ordinal >= getOpaqueGenericParams().size())
7839+
return nullptr;
7840+
7841+
auto genericParamType = getOpaqueGenericParams()[ordinal];
7842+
return genericParamType->getDecl();
7843+
}
7844+
78327845
unsigned OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(
78337846
OpaqueReturnTypeRepr *repr) const {
78347847
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ Type ConstraintSystem::openOpaqueType(OpaqueTypeArchetypeType *opaque,
846846
if (knownReplacements != OpenedTypes.end()) {
847847
auto param = opaque->getInterfaceType()->castTo<GenericTypeParamType>();
848848
for (const auto &replacement : knownReplacements->second) {
849-
if (replacement.first == param)
849+
if (replacement.first->isEqual(param))
850850
return replacement.second;
851851
}
852852

lib/Sema/MiscDiagnostics.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26102610
OpaqueTypeDecl *OpaqueDecl;
26112611
BraceStmt *Body;
26122612
SmallVector<Candidate, 4> Candidates;
2613+
SmallPtrSet<const void *, 4> KnownCandidates;
26132614

26142615
bool HasInvalidReturn = false;
26152616

@@ -2644,13 +2645,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26442645
// Check whether all of the underlying type candidates match up.
26452646
// TODO [OPAQUE SUPPORT]: diagnose multiple opaque types
26462647
SubstitutionMap underlyingSubs = Candidates.front().second;
2647-
SubstitutionMap canonicalUnderlyingSubs = underlyingSubs.getCanonical();
2648-
bool mismatch =
2649-
std::any_of(Candidates.begin() + 1, Candidates.end(),
2650-
[&](Candidate &otherCandidate) {
2651-
return canonicalUnderlyingSubs != otherCandidate.second.getCanonical();
2652-
});
2653-
if (mismatch) {
2648+
if (Candidates.size() > 1) {
26542649
unsigned mismatchIndex = OpaqueDecl->getOpaqueGenericParams().size();
26552650
for (auto genericParam : OpaqueDecl->getOpaqueGenericParams()) {
26562651
unsigned index = genericParam->getIndex();
@@ -2669,12 +2664,21 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26692664
break;
26702665
}
26712666
assert(mismatchIndex < OpaqueDecl->getOpaqueGenericParams().size());
2672-
TypeRepr *opaqueRepr =
2673-
OpaqueDecl->getOpaqueReturnTypeReprs()[mismatchIndex];
2674-
Implementation->diagnose(
2675-
diag::opaque_type_mismatched_underlying_type_candidates,
2676-
opaqueRepr)
2677-
.highlight(opaqueRepr->getSourceRange());
2667+
2668+
if (auto genericParam =
2669+
OpaqueDecl->getExplicitGenericParam(mismatchIndex)) {
2670+
Implementation->diagnose(
2671+
diag::opaque_type_mismatched_underlying_type_candidates_named,
2672+
genericParam->getName())
2673+
.highlight(genericParam->getLoc());
2674+
} else {
2675+
TypeRepr *opaqueRepr =
2676+
OpaqueDecl->getOpaqueReturnTypeReprs()[mismatchIndex];
2677+
Implementation->diagnose(
2678+
diag::opaque_type_mismatched_underlying_type_candidates,
2679+
opaqueRepr)
2680+
.highlight(opaqueRepr->getSourceRange());
2681+
}
26782682

26792683
for (auto candidate : Candidates) {
26802684
Ctx.Diags.diagnose(
@@ -2712,8 +2716,12 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
27122716

27132717
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
27142718
if (auto underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(E)) {
2715-
Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(),
2716-
underlyingToOpaque->substitutions));
2719+
auto key =
2720+
underlyingToOpaque->substitutions.getCanonical().getOpaqueValue();
2721+
if (KnownCandidates.insert(key).second) {
2722+
Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(),
2723+
underlyingToOpaque->substitutions));
2724+
}
27172725
return {false, E};
27182726
}
27192727
return {true, E};

lib/Sema/TypeCheckType.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,20 @@ TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc,
284284
return dc->getASTContext().getBridgedToObjC(dc, valueType);
285285
}
286286

287+
/// Retrieve the identity form of the opaque type archetype type.
288+
static OpaqueTypeArchetypeType *getIdentityOpaqueTypeArchetypeType(
289+
OpaqueTypeDecl *opaqueDecl, unsigned ordinal) {
290+
auto outerGenericSignature = opaqueDecl->getNamingDecl()
291+
->getInnermostDeclContext()
292+
->getGenericSignatureOfContext();
293+
294+
SubstitutionMap subs;
295+
if (outerGenericSignature)
296+
subs = outerGenericSignature->getIdentitySubstitutionMap();
297+
298+
return OpaqueTypeArchetypeType::get(opaqueDecl, ordinal, subs);
299+
}
300+
287301
Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
288302
DeclContext *foundDC,
289303
bool isSpecialized) const {
@@ -292,6 +306,16 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
292306

293307
// If we found a generic parameter, map to the archetype if there is one.
294308
if (auto genericParam = dyn_cast<GenericTypeParamDecl>(typeDecl)) {
309+
// If this generic parameter is for an opaque type, map to the opened
310+
// archetype.
311+
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(getDeclContext())) {
312+
if (genericParam->getDepth() ==
313+
opaqueDecl->getOpaqueGenericParams().front()->getDepth()) {
314+
return getIdentityOpaqueTypeArchetypeType(
315+
opaqueDecl, genericParam->getIndex());
316+
}
317+
}
318+
295319
return genericParam->getDeclaredInterfaceType();
296320
}
297321

@@ -2047,16 +2071,8 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
20472071
auto *DC = getDeclContext();
20482072
if (isa<OpaqueTypeDecl>(DC)) {
20492073
auto opaqueDecl = cast<OpaqueTypeDecl>(DC);
2050-
auto outerGenericSignature = opaqueDecl->getNamingDecl()
2051-
->getInnermostDeclContext()
2052-
->getGenericSignatureOfContext();
2053-
2054-
SubstitutionMap subs;
2055-
if (outerGenericSignature)
2056-
subs = outerGenericSignature->getIdentitySubstitutionMap();
2057-
20582074
unsigned ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr);
2059-
return OpaqueTypeArchetypeType::get(opaqueDecl, ordinal, subs);
2075+
return getIdentityOpaqueTypeArchetypeType(opaqueDecl, ordinal);
20602076
}
20612077

20622078
// We are not inside an `OpaqueTypeDecl`, so diagnose an error.

test/type/opaque_return_named.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,76 @@ func test_h0() {
4444
// Make sure we can treat h0 as a P
4545
let _: P = h0()
4646
}
47+
48+
protocol P1 {
49+
associatedtype A
50+
}
51+
52+
protocol Q1 {
53+
associatedtype B
54+
}
55+
56+
extension Int: P1 {
57+
typealias A = String
58+
}
59+
60+
extension String: P1 {
61+
typealias A = String
62+
}
63+
64+
extension Double: Q1 {
65+
typealias B = String
66+
}
67+
68+
extension String: Q1 {
69+
typealias B = Int
70+
}
71+
72+
func f1() -> <T: P1, U where U: Q1> (T, U) {
73+
return (3, 3.14159)
74+
}
75+
76+
func f2(cond: Bool) -> <T: P1, U where U: Q1> (T, U) {
77+
if cond {
78+
return (3, 3.14159)
79+
} else {
80+
return (3, 2.71828)
81+
}
82+
}
83+
84+
func f3(cond: Bool) -> <T: P1, U where U: Q1> (T, U) {
85+
// expected-error@-1{{function declares an opaque return type 'U', but the return statements in its body do not have matching underlying types}}
86+
if cond {
87+
return (3, 3.14159) // expected-note{{return statement has underlying type 'Double'}}
88+
} else {
89+
return (3, "hello") // expected-note{{return statement has underlying type 'String'}}
90+
}
91+
}
92+
93+
func f4(cond: Bool) -> <T: P1, U where U: Q1> (T, U) {
94+
if cond {
95+
return (3, 3.14159)
96+
} else {
97+
}
98+
}
99+
100+
func f5(cond: Bool) -> <T: P1, U where U: Q1> (T, U) { }
101+
// expected-error@-1{{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}}
102+
103+
struct Generic<T: P1 & Equatable> {
104+
var value: T
105+
106+
func f() -> <U: Q1, V: P1 where U: Equatable, V: Equatable> (U, V) {
107+
return ("hello", value)
108+
}
109+
}
110+
111+
func testGeneric(gs: Generic<String>, gi: Generic<Int>) {
112+
let gs1 = gs.f()
113+
let gs2 = gs.f()
114+
_ = (gs1 == gs2)
115+
116+
let gi1 = gi.f()
117+
// FIXME: Diagnostic below is correct, but a bit misleading because these are different Us and Vs.
118+
_ = (gs1 == gi1) // expected-error{{binary operator '==' cannot be applied to operands of type '(U, V)' and '(U, V)'}}
119+
}

0 commit comments

Comments
 (0)