Skip to content

Commit 3892268

Browse files
committed
[Named opaque types] Build a generic signature for named opaque types.
For a declaration that has a named opaque type, create a complete generic signature describing the opaque type that includes the specified generic parameters and any requirements within that generic parameter list. This aligns the representation of named opaque types with unnamed ('some P') opaque result types, so we get the same inference behavior. Introduce an egregious hack in type resolution to fake name lookup for the generic parameters that are specified as part of a named opaque type. This is to be replaced with the proper name lookup mechanism to find those generic parameters, such that type resolution can turn them into opaque type archetypes. There are also a number of issues with structural opaque types that are brought to the fore by named opaque types, especially the fact that OpaqueTypeDecl does not retain the mapping from specific instances of `some` to the corresponding (implicit) generic parameter, and generally assumes that the underlying type of the OpaqueTypeDecl itself is a generic type parameter. Addressing these shortcomings will benefit both features.
1 parent 340a2b7 commit 3892268

File tree

4 files changed

+188
-88
lines changed

4 files changed

+188
-88
lines changed

lib/AST/TypeRepr.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ bool TypeRepr::findIf(llvm::function_ref<bool(TypeRepr *)> pred) {
9898
// TODO [OPAQUE SUPPORT]: We should probably use something like `Type`'s
9999
// `RecursiveProperties` to track this instead of computing it.
100100
bool TypeRepr::hasOpaque() {
101-
// TODO [OPAQUE SUPPORT]: In the future we will also need to check if `this`
102-
// is a `NamedOpaqueReturnTypeRepr`.
103-
return findIf([](TypeRepr *ty) { return isa<OpaqueReturnTypeRepr>(ty); });
101+
return isa<NamedOpaqueReturnTypeRepr>(this) ||
102+
findIf([](TypeRepr *ty) { return isa<OpaqueReturnTypeRepr>(ty); });
104103
}
105104

106105
SourceLoc TypeRepr::findUncheckedAttrLoc() const {

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 110 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -178,93 +178,129 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
178178
? outerGenericSignature.getGenericParams().back()->getDepth() + 1
179179
: 0;
180180

181-
SmallVector<GenericTypeParamType *, 2> genericParamTypes;
182-
SmallVector<Requirement, 2> requirements;
183-
184-
auto opaqueReprs = collectOpaqueReturnTypeReprs(repr);
185-
if (opaqueReprs.size() > 1) {
186-
ctx.Diags.diagnose(repr->getLoc(), diag::more_than_one_opaque_type, repr);
187-
return nullptr;
188-
}
189-
190-
// TODO [OPAQUE SUPPORT]: right now we only allow one structural 'some' type,
191-
// but *very* soon we will allow more than one such type.
192-
for (unsigned i = 0; i < opaqueReprs.size(); ++i) {
193-
auto *currentRepr = opaqueReprs[i];
194-
195-
// Usually, we resolve the opaque constraint and bail if it isn't a class or
196-
// existential type (see below). However, in this case we know we will fail,
197-
// so we can bail early and provide a better diagnostic.
198-
if (auto *optionalRepr =
199-
dyn_cast<OptionalTypeRepr>(currentRepr->getConstraint())) {
200-
std::string buf;
201-
llvm::raw_string_ostream stream(buf);
202-
stream << "(some " << optionalRepr->getBase() << ")?";
203-
204-
ctx.Diags.diagnose(currentRepr->getLoc(),
205-
diag::opaque_type_invalid_constraint);
206-
ctx.Diags
207-
.diagnose(currentRepr->getLoc(), diag::opaque_of_optional_rewrite)
208-
.fixItReplaceChars(currentRepr->getStartLoc(),
209-
currentRepr->getEndLoc(), stream.str());
181+
// Determine the context of the opaque type declaration we'll be creating.
182+
auto parentDC = originatingDecl->getDeclContext();
183+
auto originatingGenericContext = originatingDecl->getAsGenericContext();
184+
GenericParamList *genericParams;
185+
GenericSignature interfaceSignature;
186+
187+
// FIXME: The generic parameter that serves as the underlying type of the
188+
// opaque result type. This should become unnecessary, once we have fully
189+
// implemented structural opaque result types.
190+
GenericTypeParamType *underlyingGenericParamType;
191+
192+
CollectedOpaqueReprs opaqueReprs;
193+
if (auto namedOpaque = dyn_cast<NamedOpaqueReturnTypeRepr>(repr)) {
194+
// Produce the generic signature for the opaque type.
195+
genericParams = namedOpaque->getGenericParams();
196+
genericParams->setDepth(opaqueSignatureDepth);
197+
198+
InferredGenericSignatureRequest request{
199+
originatingDC->getParentModule(),
200+
outerGenericSignature.getPointer(),
201+
genericParams,
202+
WhereClauseOwner(originatingDC, genericParams),
203+
/*addedRequirements=*/{},
204+
/*inferenceSources=*/{},
205+
/*allowConcreteGenericParams=*/false};
206+
207+
interfaceSignature = evaluateOrDefault(
208+
ctx.evaluator, request, GenericSignatureWithError())
209+
.getPointer();
210+
if (!interfaceSignature) {
211+
// Already produced an error.
210212
return nullptr;
211213
}
212214

213-
auto *paramType = GenericTypeParamType::get(/*type sequence*/ false,
214-
opaqueSignatureDepth, i, ctx);
215-
genericParamTypes.push_back(paramType);
216-
217-
// Try to resolve the constraint repr in the parent decl context. It should
218-
// be some kind of existential type. Pass along the error type if resolving
219-
// the repr failed.
220-
auto constraintType = TypeResolution::forInterface(
221-
dc, TypeResolverContext::GenericRequirement,
222-
// Unbound generics and placeholders are
223-
// meaningless in opaque types.
224-
/*unboundTyOpener*/ nullptr,
225-
/*placeholderHandler*/ nullptr)
226-
.resolveType(currentRepr->getConstraint());
227-
228-
if (constraintType->hasError())
229-
return nullptr;
230-
231-
// Error out if the constraint type isn't a class or existential type.
232-
if (!constraintType->getClassOrBoundGenericClass() &&
233-
!constraintType->isExistentialType()) {
234-
ctx.Diags.diagnose(currentRepr->getLoc(),
235-
diag::opaque_type_invalid_constraint);
215+
underlyingGenericParamType = interfaceSignature.getGenericParams()[0];
216+
} else {
217+
opaqueReprs = collectOpaqueReturnTypeReprs(repr);
218+
if (opaqueReprs.size() > 1) {
219+
ctx.Diags.diagnose(repr->getLoc(), diag::more_than_one_opaque_type, repr);
236220
return nullptr;
237221
}
238222

239-
if (constraintType->hasArchetype())
240-
constraintType = constraintType->mapTypeOutOfContext();
223+
// TODO [OPAQUE SUPPORT]: right now we only allow one structural 'some' type,
224+
// but *very* soon we will allow more than one such type.
225+
SmallVector<GenericTypeParamType *, 2> genericParamTypes;
226+
SmallVector<Requirement, 2> requirements;
227+
for (unsigned i = 0; i < opaqueReprs.size(); ++i) {
228+
auto *currentRepr = opaqueReprs[i];
229+
230+
// Usually, we resolve the opaque constraint and bail if it isn't a class
231+
// or existential type (see below). However, in this case we know we will
232+
// fail, so we can bail early and provide a better diagnostic.
233+
if (auto *optionalRepr =
234+
dyn_cast<OptionalTypeRepr>(currentRepr->getConstraint())) {
235+
std::string buf;
236+
llvm::raw_string_ostream stream(buf);
237+
stream << "(some " << optionalRepr->getBase() << ")?";
238+
239+
ctx.Diags.diagnose(currentRepr->getLoc(),
240+
diag::opaque_type_invalid_constraint);
241+
ctx.Diags
242+
.diagnose(currentRepr->getLoc(), diag::opaque_of_optional_rewrite)
243+
.fixItReplaceChars(currentRepr->getStartLoc(),
244+
currentRepr->getEndLoc(), stream.str());
245+
return nullptr;
246+
}
241247

242-
if (constraintType->getClassOrBoundGenericClass()) {
243-
requirements.push_back(
244-
Requirement(RequirementKind::Superclass, paramType, constraintType));
245-
} else {
246-
// In this case, the constraint type is an existential
247-
requirements.push_back(
248-
Requirement(RequirementKind::Conformance, paramType, constraintType));
248+
auto *paramType = GenericTypeParamType::get(/*type sequence*/ false,
249+
opaqueSignatureDepth, i, ctx);
250+
genericParamTypes.push_back(paramType);
251+
252+
// Try to resolve the constraint repr in the parent decl context. It
253+
// should be some kind of existential type. Pass along the error type if
254+
// resolving the repr failed.
255+
auto constraintType = TypeResolution::forInterface(
256+
dc, TypeResolverContext::GenericRequirement,
257+
// Unbound generics and placeholders are
258+
// meaningless in opaque types.
259+
/*unboundTyOpener*/ nullptr,
260+
/*placeholderHandler*/ nullptr)
261+
.resolveType(currentRepr->getConstraint());
262+
263+
if (constraintType->hasError())
264+
return nullptr;
265+
266+
// Error out if the constraint type isn't a class or existential type.
267+
if (!constraintType->getClassOrBoundGenericClass() &&
268+
!constraintType->isExistentialType()) {
269+
ctx.Diags.diagnose(currentRepr->getLoc(),
270+
diag::opaque_type_invalid_constraint);
271+
return nullptr;
272+
}
273+
274+
if (constraintType->hasArchetype())
275+
constraintType = constraintType->mapTypeOutOfContext();
276+
277+
if (constraintType->getClassOrBoundGenericClass()) {
278+
requirements.push_back(
279+
Requirement(RequirementKind::Superclass, paramType,
280+
constraintType));
281+
} else {
282+
// In this case, the constraint type is an existential
283+
requirements.push_back(
284+
Requirement(RequirementKind::Conformance, paramType,
285+
constraintType));
286+
}
249287
}
250-
}
251288

252-
auto interfaceSignature = buildGenericSignature(ctx, outerGenericSignature,
253-
genericParamTypes,
254-
std::move(requirements));
289+
interfaceSignature = buildGenericSignature(ctx, outerGenericSignature,
290+
genericParamTypes,
291+
std::move(requirements));
292+
genericParams = originatingGenericContext
293+
? originatingGenericContext->getGenericParams()
294+
: nullptr;
295+
underlyingGenericParamType = genericParamTypes[0];
296+
}
255297

256298
// Create the OpaqueTypeDecl for the result type.
257-
// It has the same parent context and generic environment as the originating
258-
// decl.
259-
auto parentDC = originatingDecl->getDeclContext();
260-
auto originatingGenericContext = originatingDecl->getAsGenericContext();
261-
GenericParamList *genericParams = originatingGenericContext
262-
? originatingGenericContext->getGenericParams()
263-
: nullptr;
264-
265299
auto opaqueDecl = new (ctx)
266300
OpaqueTypeDecl(originatingDecl, genericParams, parentDC,
267-
interfaceSignature, opaqueReprs[0], genericParamTypes[0]);
301+
interfaceSignature,
302+
opaqueReprs.empty() ? 0 : opaqueReprs[0],
303+
underlyingGenericParamType);
268304
opaqueDecl->copyFormalAccessFrom(originatingDecl);
269305
if (auto originatingSig = originatingDC->getGenericSignatureOfContext()) {
270306
opaqueDecl->setGenericSignature(originatingSig);

lib/Sema/TypeCheckType.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,9 +2084,47 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
20842084
return resolveExistentialType(existential, options);
20852085
}
20862086

2087-
case TypeReprKind::NamedOpaqueReturn:
2088-
return resolveType(cast<NamedOpaqueReturnTypeRepr>(repr)->getBase(),
2089-
options);
2087+
case TypeReprKind::NamedOpaqueReturn: {
2088+
// If the named opaque result type is in a valid position, resolution
2089+
// should happen in the context of an `OpaqueTypeDecl`. This decl is
2090+
// implicit in the source and is created in such contexts by evaluation of
2091+
// an `OpaqueResultTypeRequest`.
2092+
auto opaqueRepr = cast<NamedOpaqueReturnTypeRepr>(repr);
2093+
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(getDeclContext())) {
2094+
// Resolve the base type within this context.
2095+
2096+
// FIXME: Temporary hack to resolve an identifier type to one of the
2097+
// generic parameters of the opaque return type. This should be subsumed
2098+
// be proper name lookup, OF COURSE.
2099+
if (auto simpleIdent = dyn_cast<SimpleIdentTypeRepr>(
2100+
opaqueRepr->getBase())) {
2101+
Identifier name = simpleIdent->getComponentRange().front()
2102+
->getNameRef().getBaseIdentifier();
2103+
if (auto gpDecl = opaqueRepr->getGenericParams()
2104+
->lookUpGenericParam(name)) {
2105+
auto outerGenericSignature = opaqueDecl->getNamingDecl()
2106+
->getInnermostDeclContext()
2107+
->getGenericSignatureOfContext();
2108+
2109+
SubstitutionMap subs;
2110+
if (outerGenericSignature)
2111+
subs = outerGenericSignature->getIdentitySubstitutionMap();
2112+
2113+
return OpaqueTypeArchetypeType::get(
2114+
opaqueDecl, gpDecl->getIndex(), subs);
2115+
}
2116+
}
2117+
2118+
return resolveType(opaqueRepr->getBase(), options);
2119+
}
2120+
2121+
// We are not inside an `OpaqueTypeDecl`, so diagnose an error.
2122+
if (!(options & TypeResolutionFlags::SilenceErrors)) {
2123+
diagnose(repr->getStartLoc(), diag::unsupported_opaque_type);
2124+
}
2125+
2126+
return ErrorType::get(getASTContext());
2127+
}
20902128

20912129
case TypeReprKind::Placeholder: {
20922130
auto &ctx = getASTContext();

test/type/opaque_return_named.swift

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,45 @@
22

33
// Tests for experimental extensions to opaque return type support.
44

5-
func f0() -> <T> () { }
6-
func f1() -> <T, U, V> () { }
7-
func f2() -> <T: Collection, U: SignedInteger> () { }
8-
func f4() async -> <T> () { }
5+
func f0() -> <T> T { return () }
6+
func f1() -> <T, U, V> T { () }
7+
// FIXME better diagnostic: expected-error@-1{{generic parameter 'U' could not be inferred}}
8+
// FIXME better diagnostic: expected-error@-2{{generic parameter 'V' could not be inferred}}
9+
func f2() -> <T: Collection, U: SignedInteger> T { // expected-note{{required by opaque return type of global function 'f2()'}}
10+
() // expected-error{{type '()' cannot conform to 'Collection'}}
11+
// expected-note@-1{{only concrete types such as structs, enums and classes can conform to protocols}}
12+
// expected-error@-2{{generic parameter 'U' could not be inferred}}
13+
}
14+
15+
func f4() async -> <T> T { () }
916

1017
func g0() -> <T> { } // expected-error{{expected type for function result}}
11-
func g1() -> async <T> () { } // expected-error{{'async' may only occur before '->'}}
12-
func g2() -> <T> () async { } // expected-error{{'async' may only occur before '->'}}
18+
func g1() -> async <T> T { () } // expected-error{{'async' may only occur before '->'}}
19+
func g2() -> <T> T async { () } // expected-error{{'async' may only occur before '->'}}
1320

1421
let x0: <T> Int = 1
1522
var x1: <T> (Int, Int) = (1, 1)
1623
var x2: <T> (<U> Int, Int) = (1, 1) // expected-error{{expected type}} expected-error{{cannot convert value of type '(Int, Int)' to specified type 'Int'}}
17-
for _: <T> Int in [1, 2, 3] { }
24+
for _: <T> Int in [1, 2, 3] { } // FIXME mention of 'some' is wrong: expected-error{{some' type can only be declared on a single property declaration}}
25+
26+
struct S0 { subscript(i: Int) -> <T> T { 1 } }
27+
28+
protocol P { }
29+
extension Int: P { }
30+
31+
protocol Q { }
32+
33+
func h0() -> <T: P> T { 5 }
34+
35+
func h1() -> <T: P> T { // expected-note{{opaque return type declared here}}
36+
3.14159 // expected-error{{return type of global function 'h1()' requires that 'Double' conform to 'P'}}
37+
}
38+
39+
func h2() -> <T: P & Q> T { // expected-note{{opaque return type declared here}}
40+
3 // expected-error{{return type of global function 'h2()' requires that 'Int' conform to 'Q'}}
41+
}
1842

19-
struct S0 { subscript(i: Int) -> <T> Int { 1 } }
43+
func test_h0() {
44+
// Make sure we can treat h0 as a P
45+
let _: P = h0()
46+
}

0 commit comments

Comments
 (0)