Skip to content

Commit 4c9bca9

Browse files
committed
Implement basic support for opaque parameters.
Implement function parameters of the form `some P` be synthesizing an implicit generic parameter whose requirements come from the opaque type. We then map the opaque type back to the generic parameter, and print as the opaque type. This allows us to write functions with implicit generic parameters: ```swift func f(_: some Collection) { } ``` which is equivalent to: ```swift func f<T: Collection>(_: some T) { } ``` where `T` is an otherwise-unused generic parameter name. All of this is behind the experimental frontend flag `-enable-experimental-opaque-parameters`.
1 parent 11a0277 commit 4c9bca9

File tree

6 files changed

+180
-10
lines changed

6 files changed

+180
-10
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2789,7 +2789,8 @@ class OpaqueTypeDecl final :
27892789
/// Get the ordinal of the anonymous opaque parameter of this decl with type
27902790
/// repr `repr`, as introduce implicitly by an occurrence of "some" in return
27912791
/// position e.g. `func f() -> some P`. Returns -1 if `repr` is not found.
2792-
unsigned getAnonymousOpaqueParamOrdinal(OpaqueReturnTypeRepr *repr) const;
2792+
Optional<unsigned> getAnonymousOpaqueParamOrdinal(
2793+
OpaqueReturnTypeRepr *repr) const;
27932794

27942795
GenericSignature getOpaqueInterfaceGenericSignature() const {
27952796
return OpaqueInterfaceGenericSignature;

lib/AST/ASTPrinter.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6031,7 +6031,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
60316031
}
60326032

60336033
void visitGenericTypeParamType(GenericTypeParamType *T) {
6034-
if (T->getDecl() == nullptr) {
6034+
auto decl = T->getDecl();
6035+
if (!decl) {
60356036
// If we have an alternate name for this type, use it.
60366037
if (Options.AlternativeTypeNames) {
60376038
auto found = Options.AlternativeTypeNames->find(T->getCanonicalType());
@@ -6047,6 +6048,25 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
60476048
T = Options.GenericSig->getSugaredType(T);
60486049
}
60496050

6051+
// Print opaque types as "some ..."
6052+
if (decl && decl->isOpaqueType()) {
6053+
// If we have and should print based on the type representation, do so.
6054+
if (auto opaqueRepr = decl->getOpaqueTypeRepr()) {
6055+
if (willUseTypeReprPrinting(opaqueRepr, Type(), Options)) {
6056+
opaqueRepr->print(Printer, Options);
6057+
return;
6058+
}
6059+
}
6060+
6061+
// Print based on the type.
6062+
Printer << "some ";
6063+
if (auto inheritedType = decl->getInherited().front().getType())
6064+
inheritedType->print(Printer, Options);
6065+
else
6066+
Printer << "Any";
6067+
return;
6068+
}
6069+
60506070
const auto Name = T->getName();
60516071
if (Name.empty()) {
60526072
Printer << "<anonymous>";

lib/AST/Decl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4113,7 +4113,7 @@ GenericTypeParamDecl::GenericTypeParamDecl(
41134113
assert(Bits.GenericTypeParamDecl.Index == index && "Truncation");
41144114
Bits.GenericTypeParamDecl.TypeSequence = isTypeSequence;
41154115
Bits.GenericTypeParamDecl.IsOpaqueType = isOpaqueType;
4116-
assert(!isOpaqueType || !opaqueTypeRepr);
4116+
assert(isOpaqueType || !opaqueTypeRepr);
41174117
if (isOpaqueType)
41184118
*getTrailingObjects<OpaqueReturnTypeRepr *>() = opaqueTypeRepr;
41194119

@@ -7877,15 +7877,15 @@ GenericTypeParamDecl *OpaqueTypeDecl::getExplicitGenericParam(
78777877
return genericParamType->getDecl();
78787878
}
78797879

7880-
unsigned OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(
7880+
Optional<unsigned> OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(
78817881
OpaqueReturnTypeRepr *repr) const {
78827882
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&
78837883
"can't do opaque param lookup without underlying interface repr");
78847884
auto opaqueReprs = getOpaqueReturnTypeReprs();
78857885
auto found = std::find(opaqueReprs.begin(), opaqueReprs.end(), repr);
78867886
if (found != opaqueReprs.end())
78877887
return found - opaqueReprs.begin();
7888-
return -1;
7888+
return None;
78897889
}
78907890

78917891
Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const {

lib/AST/NameLookup.cpp

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2567,6 +2567,63 @@ createExtensionGenericParams(ASTContext &ctx,
25672567
return toParams;
25682568
}
25692569

2570+
/// If there are opaque parameters in the given declaration, create the
2571+
/// generic parameters associated with them.
2572+
static SmallVector<GenericTypeParamDecl *, 2>
2573+
createOpaqueParameterGenericParams(
2574+
GenericContext *genericContext, GenericParamList *parsedGenericParams) {
2575+
ASTContext &ctx = genericContext->getASTContext();
2576+
if (!ctx.LangOpts.EnableExperimentalOpaqueParameters)
2577+
return { };
2578+
2579+
auto value = dyn_cast_or_null<ValueDecl>(genericContext->getAsDecl());
2580+
if (!value)
2581+
return { };
2582+
2583+
// Functions, initializers, and subscripts can contain opaque parameters.
2584+
ParameterList *params = nullptr;
2585+
if (auto func = dyn_cast<AbstractFunctionDecl>(value))
2586+
params = func->getParameters();
2587+
else if (auto subscript = dyn_cast<SubscriptDecl>(value))
2588+
params = subscript->getIndices();
2589+
else
2590+
return { };
2591+
2592+
// Look for parameters that have "some" types in them.
2593+
unsigned index = parsedGenericParams ? parsedGenericParams->size() : 0;
2594+
SmallVector<GenericTypeParamDecl *, 2> implicitGenericParams;
2595+
auto dc = value->getInnermostDeclContext();
2596+
for (auto param : *params) {
2597+
// Don't permit variadic or inout parameters.
2598+
if (param->isVariadic() || param->isInOut())
2599+
continue;
2600+
2601+
auto typeRepr = param->getTypeRepr();
2602+
if (!typeRepr)
2603+
continue;
2604+
2605+
// FIXME: Do we want to allow any structure here? Optionals?
2606+
auto opaqueRepr = dyn_cast<OpaqueReturnTypeRepr>(typeRepr);
2607+
if (!opaqueRepr)
2608+
continue;
2609+
2610+
// Allocate a new generic parameter to represent this opaque type.
2611+
auto gp = GenericTypeParamDecl::create(
2612+
dc, Identifier(), SourceLoc(), /*isTypeSequence=*/false,
2613+
GenericTypeParamDecl::InvalidDepth, index++, /*isOpaqueType=*/true,
2614+
opaqueRepr);
2615+
gp->setImplicit();
2616+
2617+
// Use the underlying constraint as the constraint on the generic parameter.
2618+
InheritedEntry inherited[1] = { { TypeLoc(opaqueRepr->getConstraint()) } };
2619+
gp->setInherited(ctx.AllocateCopy(inherited));
2620+
2621+
implicitGenericParams.push_back(gp);
2622+
}
2623+
2624+
return implicitGenericParams;
2625+
}
2626+
25702627
GenericParamList *
25712628
GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) const {
25722629
if (auto *ext = dyn_cast<ExtensionDecl>(value)) {
@@ -2619,7 +2676,35 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c
26192676
return result;
26202677
}
26212678

2622-
return value->getParsedGenericParams();
2679+
auto parsedGenericParams = value->getParsedGenericParams();
2680+
2681+
// Create implicit generic parameters due to opaque parameters, if we need
2682+
// them.
2683+
auto implicitGenericParams =
2684+
createOpaqueParameterGenericParams(value, parsedGenericParams);
2685+
if (implicitGenericParams.empty())
2686+
return parsedGenericParams;
2687+
2688+
// If there were no parsed generic parameters, create a fully-implicit
2689+
// generic parameter list.
2690+
ASTContext &ctx = value->getASTContext();
2691+
if (!parsedGenericParams) {
2692+
return GenericParamList::create(
2693+
ctx, SourceLoc(), implicitGenericParams, SourceLoc());
2694+
}
2695+
2696+
// Combine the existing generic parameters with the implicit ones.
2697+
SmallVector<GenericTypeParamDecl *, 4> allGenericParams;
2698+
allGenericParams.reserve(
2699+
parsedGenericParams->size() + implicitGenericParams.size());
2700+
allGenericParams.append(parsedGenericParams->begin(),
2701+
parsedGenericParams->end());
2702+
allGenericParams.append(implicitGenericParams);
2703+
return GenericParamList::create(
2704+
ctx, parsedGenericParams->getLAngleLoc(), allGenericParams,
2705+
parsedGenericParams->getWhereLoc(),
2706+
parsedGenericParams->getRequirements(),
2707+
parsedGenericParams->getRAngleLoc());
26232708
}
26242709

26252710
NominalTypeDecl *

lib/Sema/TypeCheckType.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,10 +2108,22 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
21082108
// evaluation of an `OpaqueResultTypeRequest`.
21092109
auto opaqueRepr = cast<OpaqueReturnTypeRepr>(repr);
21102110
auto *DC = getDeclContext();
2111-
if (isa<OpaqueTypeDecl>(DC)) {
2112-
auto opaqueDecl = cast<OpaqueTypeDecl>(DC);
2113-
unsigned ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr);
2114-
return getIdentityOpaqueTypeArchetypeType(opaqueDecl, ordinal);
2111+
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(DC)) {
2112+
if (auto ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr))
2113+
return getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal);
2114+
}
2115+
2116+
// Check whether any of the generic parameters in the context represents
2117+
// this opaque type. If so, return that generic parameter.
2118+
if (auto declDC = DC->getAsDecl()) {
2119+
if (auto genericContext = declDC->getAsGenericContext()) {
2120+
if (auto genericParams = genericContext->getGenericParams()) {
2121+
for (auto genericParam : *genericParams) {
2122+
if (genericParam->getOpaqueTypeRepr() == opaqueRepr)
2123+
return genericParam->getDeclaredInterfaceType();
2124+
}
2125+
}
2126+
}
21152127
}
21162128

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

test/type/opaque_parameters.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-opaque-parameters -disable-availability-checking
2+
3+
protocol P { }
4+
5+
protocol Q {
6+
associatedtype A: P & Equatable
7+
8+
func f() -> A
9+
}
10+
11+
extension Int: P { }
12+
extension String: P { }
13+
14+
// expected-note@+1{{requirement from conditional conformance of '[Double]' to 'Q'}}
15+
extension Array: Q where Element: P, Element: Equatable {
16+
func f() -> Element {
17+
return first!
18+
}
19+
}
20+
21+
extension Set: Q where Element: P, Element: Equatable {
22+
func f() -> Element {
23+
return first!
24+
}
25+
}
26+
27+
// expected-note@+2{{where 'some Q' = 'Int'}}
28+
// expected-note@+1{{in call to function 'takesQ'}}
29+
func takesQ(_ q: some Q) -> Bool {
30+
return q.f() == q.f()
31+
}
32+
33+
func testTakesQ(arrayOfInts: [Int], setOfStrings: Set<String>, i: Int) {
34+
_ = takesQ(arrayOfInts)
35+
_ = takesQ(setOfStrings)
36+
_ = takesQ(i) // expected-error{{global function 'takesQ' requires that 'Int' conform to 'Q'}}
37+
38+
let f = takesQ // expected-error{{generic parameter 'some Q' could not be inferred}}
39+
let _: ([String]) -> Bool = takesQ
40+
let _: ([Double]) -> Bool = takesQ // expected-error{{global function 'takesQ' requires that 'Double' conform to 'P'}}
41+
_ = f
42+
}
43+
44+
// expected-note@+1{{where 'some P' = '[Int]'}}
45+
func takeMultiple<T>(_: T, _: some Q, _: some P) { }
46+
47+
func testTakeMultiple(
48+
arrayOfInts: [Int], setOfStrings: Set<String>, i: Int, d: Double
49+
) {
50+
takeMultiple(d, arrayOfInts, i)
51+
takeMultiple(d, arrayOfInts, arrayOfInts) // expected-error{{global function 'takeMultiple' requires that '[Int]' conform to 'P'}}
52+
}

0 commit comments

Comments
 (0)