Skip to content

Structural opaque result types #40710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ CHANGELOG

_**Note:** This is in reverse chronological order, so newer entries are added to the top._

## Swift Next

* [SE-0328][]:

Opaque types (expressed with 'some') can now be used in structural positions
within a result type, including having multiple opaque types in the same
result. For example:

```
func getSomeDictionary() -> [some Hashable: some Codable] {
return [ 1: "One", 2: "Two" ]
}
```
Swift 5.6
---------

Expand Down Expand Up @@ -8761,6 +8774,7 @@ Swift 1.0
[SE-0316]: <https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md>
[SE-0324]: <https://github.com/apple/swift-evolution/blob/main/proposals/0324-c-lang-pointer-arg-conversion.md>
[SE-0323]: <https://github.com/apple/swift-evolution/blob/main/proposals/0323-async-main-semantics.md>
[SE-0328]: <https://github.com/apple/swift-evolution/blob/main/proposals/0328-structural-opaque-result-types.md>

[SR-75]: <https://bugs.swift.org/browse/SR-75>
[SR-106]: <https://bugs.swift.org/browse/SR-106>
Expand Down
12 changes: 8 additions & 4 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ class ASTMangler : public Mangler {
}

void appendBoundGenericArgs(Type type, GenericSignature sig,
bool &isFirstArgList);
bool &isFirstArgList,
const ValueDecl *forDecl = nullptr);

/// Append the bound generics arguments for the given declaration context
/// based on a complete substitution map.
Expand All @@ -349,18 +350,21 @@ class ASTMangler : public Mangler {
unsigned appendBoundGenericArgs(DeclContext *dc,
GenericSignature sig,
SubstitutionMap subs,
bool &isFirstArgList);
bool &isFirstArgList,
const ValueDecl *forDecl = nullptr);

/// Append the bound generic arguments as a flat list, disregarding depth.
void appendFlatGenericArgs(SubstitutionMap subs,
GenericSignature sig);
GenericSignature sig,
const ValueDecl *forDecl = nullptr);

/// Append any retroactive conformances.
void appendRetroactiveConformances(Type type, GenericSignature sig);
void appendRetroactiveConformances(SubstitutionMap subMap,
GenericSignature sig,
ModuleDecl *fromModule);
void appendImplFunctionType(SILFunctionType *fn, GenericSignature sig);
void appendImplFunctionType(SILFunctionType *fn, GenericSignature sig,
const ValueDecl *forDecl = nullptr);

void appendContextOf(const ValueDecl *decl);

Expand Down
71 changes: 51 additions & 20 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2699,45 +2699,63 @@ class GenericTypeDecl : public GenericContext, public TypeDecl {
/// The declared type uses a special kind of archetype type to represent
/// abstracted types, e.g. `(some P, some Q)` becomes `((opaque archetype 0),
/// (opaque archetype 1))`.
class OpaqueTypeDecl : public GenericTypeDecl {
class OpaqueTypeDecl final :
public GenericTypeDecl,
private llvm::TrailingObjects<OpaqueTypeDecl, OpaqueReturnTypeRepr *> {
friend TrailingObjects;

/// The original declaration that "names" the opaque type. Although a specific
/// opaque type cannot be explicitly named, oapque types can propagate
/// arbitrarily through expressions, so we need to know *which* opaque type is
/// propagated.
ValueDecl *NamingDecl;
///
/// The bit indicates whether there are any trailing
/// OpaqueReturnTypeReprs.
llvm::PointerIntPair<ValueDecl *, 1>
NamingDeclAndHasOpaqueReturnTypeRepr;

/// The generic signature of the opaque interface to the type. This is the
/// outer generic signature with added generic parameters representing the
/// abstracted underlying types.
GenericSignature OpaqueInterfaceGenericSignature;

/// The type repr of the underlying type. Might be null if no source location
/// is availble, e.g. if this decl was loaded from a serialized module.
OpaqueReturnTypeRepr *UnderlyingInterfaceRepr;

/// The generic parameter that represents the underlying type.
GenericTypeParamType *UnderlyingInterfaceType;

/// If known, the underlying type and conformances of the opaque type,
/// expressed as a SubstitutionMap for the opaque interface generic signature.
/// This maps types in the interface generic signature to the outer generic
/// signature of the original declaration.
Optional<SubstitutionMap> UnderlyingTypeSubstitutions;

mutable Identifier OpaqueReturnTypeIdentifier;

public:

OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
OpaqueReturnTypeRepr *UnderlyingInterfaceRepr,
GenericTypeParamType *UnderlyingInterfaceType);
ArrayRef<OpaqueReturnTypeRepr *> OpaqueReturnTypeReprs);

unsigned getNumOpaqueReturnTypeReprs() const {
return NamingDeclAndHasOpaqueReturnTypeRepr.getInt()
? getOpaqueGenericParams().size()
: 0;
}

size_t numTrailingObjects(OverloadToken<OpaqueReturnTypeRepr *>) const {
return getNumOpaqueReturnTypeReprs();
}

public:
static OpaqueTypeDecl *get(
ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
ArrayRef<OpaqueReturnTypeRepr *> OpaqueReturnTypeReprs);

ValueDecl *getNamingDecl() const { return NamingDecl; }
ValueDecl *getNamingDecl() const {
return NamingDeclAndHasOpaqueReturnTypeRepr.getPointer();
}

void setNamingDecl(ValueDecl *D) {
assert(!NamingDecl && "already have naming decl");
NamingDecl = D;
assert(!getNamingDecl() && "already have naming decl");
NamingDeclAndHasOpaqueReturnTypeRepr.setPointer(D);
}

/// Is this opaque type the opaque return type of the given function?
Expand All @@ -2754,11 +2772,24 @@ class OpaqueTypeDecl : public GenericTypeDecl {
GenericSignature getOpaqueInterfaceGenericSignature() const {
return OpaqueInterfaceGenericSignature;
}

GenericTypeParamType *getUnderlyingInterfaceType() const {
return UnderlyingInterfaceType;

/// Retrieve the generic parameters that represent the opaque types described by this opaque
/// type declaration.
TypeArrayView<GenericTypeParamType> getOpaqueGenericParams() const {
return OpaqueInterfaceGenericSignature.getInnermostGenericParams();
}


/// Retrieve the buffer containing the opaque return type
/// representations that correspond to the opaque generic parameters.
ArrayRef<OpaqueReturnTypeRepr *> getOpaqueReturnTypeReprs() const {
return {
getTrailingObjects<OpaqueReturnTypeRepr *>(),
getNumOpaqueReturnTypeReprs()
};
}

/// The substitutions that map the generic parameters of the opaque type to
/// their underlying types, when that information is known.
Optional<SubstitutionMap> getUnderlyingTypeSubstitutions() const {
return UnderlyingTypeSubstitutions;
}
Expand Down
7 changes: 2 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1876,9 +1876,6 @@ ERROR(opaque_type_invalid_constraint,none,
"and/or a base class", ())
NOTE(opaque_of_optional_rewrite,none,
"did you mean to write an optional of an 'opaque' type?", ())
ERROR(more_than_one_opaque_type,none,
"%0 contains multiple 'opaque' types, but only one 'opaque' type is "
"supported", (TypeRepr*))
ERROR(inferred_opaque_type,none,
"property definition has inferred type %0, involving the 'some' "
"return type of another declaration", (Type))
Expand Down Expand Up @@ -4135,8 +4132,8 @@ ERROR(opaque_type_no_underlying_type_candidates,none,
"function declares an opaque return type, but has no return statements "
"in its body from which to infer an underlying type", ())
ERROR(opaque_type_mismatched_underlying_type_candidates,none,
"function declares an opaque return type, but the return statements "
"in its body do not have matching underlying types", ())
"function declares an opaque return type %0, but the return statements "
"in its body do not have matching underlying types", (TypeRepr *))
NOTE(opaque_type_underlying_type_candidate_here,none,
"return statement has underlying type %0", (Type))
ERROR(opaque_type_self_referential_underlying_type,none,
Expand Down
9 changes: 7 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3003,8 +3003,13 @@ class LinearToDifferentiableFunctionExpr : public ImplicitConversionExpr {
/// (opaque type)" and "S<T> ---> S<(opaque type)>".
class UnderlyingToOpaqueExpr : public ImplicitConversionExpr {
public:
UnderlyingToOpaqueExpr(Expr *subExpr, Type ty)
: ImplicitConversionExpr(ExprKind::UnderlyingToOpaque, subExpr, ty) {}
/// The substitutions to be applied to the opaque type declaration to
/// produce the resulting type.
const SubstitutionMap substitutions;

UnderlyingToOpaqueExpr(Expr *subExpr, Type ty, SubstitutionMap substitutions)
: ImplicitConversionExpr(ExprKind::UnderlyingToOpaque, subExpr, ty),
substitutions(substitutions) {}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::UnderlyingToOpaque;
Expand Down
8 changes: 3 additions & 5 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5601,17 +5601,15 @@ class OpaqueTypeArchetypeType final : public ArchetypeType,
/// func foo() -> (some P, some Q)
///
/// then the underlying type of `some P` would be ordinal 0, and `some Q` would be ordinal 1.
unsigned getOrdinal() const {
// TODO [OPAQUE SUPPORT]: multiple opaque types
return 0;
}
unsigned getOrdinal() const;

static void Profile(llvm::FoldingSetNodeID &ID,
OpaqueTypeDecl *OpaqueDecl,
unsigned ordinal,
SubstitutionMap Substitutions);

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDecl(), getSubstitutions());
Profile(ID, getDecl(), getOrdinal(), getSubstitutions());
};

private:
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -486,10 +486,6 @@ def enable_experimental_named_opaque_types :
Flag<["-"], "enable-experimental-named-opaque-types">,
HelpText<"Enable experimental support for named opaque result types">;

def enable_experimental_structural_opaque_types :
Flag<["-"], "enable-experimental-structural-opaque-types">,
HelpText<"Enable experimental support for structural opaque result types">;

def enable_explicit_existential_types :
Flag<["-"], "enable-explicit-existential-types">,
HelpText<"Enable experimental support for explicit existential types">;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,11 @@ class ConstraintSystem {
ArrayRef<ConstraintLocator::PathElement> path,
unsigned summaryFlags);

/// Retrieve a locator for opening the opaque archetype for the given
/// opaque type.
ConstraintLocator *getOpenOpaqueLocator(
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl);

/// Retrive the constraint locator for the given anchor and
/// path, uniqued and automatically infer the summary flags
ConstraintLocator *
Expand Down
24 changes: 12 additions & 12 deletions lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1510,18 +1510,18 @@ SDKNode *swift::ide::api::
SwiftDeclCollector::constructTypeNode(Type T, TypeInitInfo Info) {
if (Ctx.checkingABI()) {
T = T->getCanonicalType();
// If the type is a opaque result type (some Type) and we're in the ABI mode,
// we should substitute the opaque result type to its underlying type.
// Notice this only works if the opaque result type is from an inlinable
// function where the function body is present in the swift module file, thus
// allowing us to know the concrete type.
if (auto OTA = T->getAs<OpaqueTypeArchetypeType>()) {
if (auto *D = OTA->getDecl()) {
if (auto SubMap = D->getUnderlyingTypeSubstitutions()) {
T = Type(D->getUnderlyingInterfaceType()).
subst(*SubMap)->getCanonicalType();
}
}

if (T->hasOpaqueArchetype()) {
// When the type contains an opaque result type and we're in the ABI mode,
// we should substitute the opaque result type to its underlying type.
// Notice this only works if the opaque result type is from an inlinable
// function where the function body is present in the swift module file,
// thus allowing us to know the concrete type.
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
/*inContext=*/nullptr, ResilienceExpansion::Maximal,
/*isWholeModuleContext=*/false);
T = T.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes)
->getCanonicalType();
}
}

Expand Down
6 changes: 2 additions & 4 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4275,17 +4275,15 @@ DependentMemberType *DependentMemberType::get(Type base,
OpaqueTypeArchetypeType *
OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, unsigned ordinal,
SubstitutionMap Substitutions) {
// TODO [OPAQUE SUPPORT]: multiple opaque types
assert(ordinal == 0 && "we only support one 'some' type per composite type");
auto opaqueParamType = Decl->getUnderlyingInterfaceType();
auto opaqueParamType = Decl->getOpaqueGenericParams()[ordinal];

// TODO: We could attempt to preserve type sugar in the substitution map.
// Currently archetypes are assumed to be always canonical in many places,
// though, so doing so would require fixing those places.
Substitutions = Substitutions.getCanonical();

llvm::FoldingSetNodeID id;
Profile(id, Decl, Substitutions);
Profile(id, Decl, ordinal, Substitutions);

auto &ctx = Decl->getASTContext();

Expand Down
5 changes: 0 additions & 5 deletions lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,6 @@ Type ASTBuilder::resolveOpaqueType(NodePointer opaqueDescriptor,
auto opaqueDecl = parentModule->lookupOpaqueResultType(mangledName);
if (!opaqueDecl)
return Type();
// TODO [OPAQUE SUPPORT]: multiple opaque types
assert(ordinal == 0 && "not implemented");
if (ordinal != 0)
return Type();

SmallVector<Type, 8> allArgs;
for (auto argSet : args) {
allArgs.append(argSet.begin(), argSet.end());
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ namespace {
OS << " naming_decl=";
printDeclName(OTD->getNamingDecl());
PrintWithColorRAII(OS, TypeColor) << " opaque_interface="
<< Type(OTD->getUnderlyingInterfaceType()).getString();
<< OTD->getDeclaredInterfaceType().getString();
OS << " in "
<< OTD->getOpaqueInterfaceGenericSignature()->getAsString();
if (auto underlyingSubs = OTD->getUnderlyingTypeSubstitutions()) {
Expand Down Expand Up @@ -3776,6 +3776,7 @@ namespace {
StringRef label) {
printArchetypeCommon(T, "opaque_type", label);
printField("decl", T->getDecl()->getNamingDecl()->printRef());
printField("ordinal", T->getOrdinal());
if (!T->getSubstitutions().empty()) {
OS << '\n';
SmallPtrSet<const ProtocolConformance *, 4> Dumped;
Expand Down
Loading