Skip to content
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

Add support for opaque result types in structural positions #38392

Merged
merged 2 commits into from
Aug 4, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 25 additions & 13 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2537,7 +2537,10 @@ class ValueDecl : public Decl {
OpaqueTypeDecl *getOpaqueResultTypeDecl() const;

/// Get the representative for this value's opaque result type, if it has one.
OpaqueReturnTypeRepr *getOpaqueResultTypeRepr() const;
/// Returns a `TypeRepr` instead of an `OpaqueReturnTypeRepr` because 'some'
/// types might appear in one or more structural positions, e.g. (some P,
/// some Q), or we might have a `NamedOpaqueReturnTypeRepr`.
TypeRepr *getOpaqueResultTypeRepr() const;

/// Retrieve the attribute associating this declaration with a
/// result builder, if there is one.
Expand Down Expand Up @@ -2629,26 +2632,30 @@ class GenericTypeDecl : public GenericContext, public TypeDecl {
/// clients of the opaque type, only exposing the type as something conforming
/// to a given set of constraints.
///
/// Currently, opaque types do not normally have an explicit spelling in source
/// code. One is formed implicitly when a declaration is written with an opaque
/// result type, as in:
/// An `OpaqueTypeDecl` is formed implicitly when a declaration is written with
/// an opaque result type, as in the following example:
///
/// func foo() -> some SignedInteger { return 1 }
///
/// The declared type is a special kind of ArchetypeType representing the
/// abstracted underlying type.
/// 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 {
/// 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 generic signature of the opaque interface to the type. This is the
/// outer generic signature with an added generic parameter representing the
/// underlying type.
/// 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;

Expand All @@ -2661,12 +2668,12 @@ class OpaqueTypeDecl : public GenericTypeDecl {
mutable Identifier OpaqueReturnTypeIdentifier;

public:
OpaqueTypeDecl(ValueDecl *NamingDecl,
GenericParamList *GenericParams,
OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
OpaqueReturnTypeRepr *UnderlyingInterfaceRepr,
GenericTypeParamType *UnderlyingInterfaceType);

ValueDecl *getNamingDecl() const { return NamingDecl; }

void setNamingDecl(ValueDecl *D) {
Expand All @@ -2680,6 +2687,11 @@ class OpaqueTypeDecl : public GenericTypeDecl {
/// function could also be the getter of a storage declaration.
bool isOpaqueReturnTypeOfFunction(const AbstractFunctionDecl *func) const;

/// Get the ordinal of the anonymous opaque parameter of this decl with type
/// repr `repr`, as introduce implicitly by an occurrence of "some" in return
/// position e.g. `func f() -> some P`. Returns -1 if `repr` is not found.
unsigned getAnonymousOpaqueParamOrdinal(OpaqueReturnTypeRepr *repr) const;

GenericSignature getOpaqueInterfaceGenericSignature() const {
return OpaqueInterfaceGenericSignature;
}
Expand Down
11 changes: 8 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,11 @@ WARNING(spi_attribute_on_import_of_public_module,none,
ERROR(opaque_type_invalid_constraint,none,
"an 'opaque' type must specify only 'Any', 'AnyObject', protocols, "
"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 @@ -4060,9 +4065,6 @@ WARNING(trailing_closure_requires_parens,none,
" statement; pass as a parenthesized argument to silence this warning",
())

ERROR(opaque_type_var_no_init,none,
"property declares an opaque return type, but has no initializer "
"expression from which to infer an underlying type", ())
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", ())
Expand All @@ -4074,6 +4076,9 @@ NOTE(opaque_type_underlying_type_candidate_here,none,
ERROR(opaque_type_self_referential_underlying_type,none,
"function opaque return type was inferred as %0, which defines the "
"opaque type in terms of itself", (Type))
ERROR(opaque_type_var_no_init,none,
"property declares an opaque return type, but has no initializer "
"expression from which to infer an underlying type", ())
ERROR(opaque_type_var_no_underlying_type,none,
"property declares an opaque return type, but cannot infer the "
"underlying type from its initializer expression", ())
Expand Down
5 changes: 3 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3193,8 +3193,9 @@ class LinearToDifferentiableFunctionExpr : public ImplicitConversionExpr {
}
};


/// Use an opaque type to abstract a value of the underlying concrete type.
/// Use an opaque type to abstract a value of the underlying concrete type,
/// possibly nested inside other types. E.g. can perform coversions "T --->
/// (opaque type)" and "S<T> ---> S<(opaque type)>".
class UnderlyingToOpaqueExpr : public ImplicitConversionExpr {
public:
UnderlyingToOpaqueExpr(Expr *subExpr, Type ty)
Expand Down
20 changes: 17 additions & 3 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,20 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr {
return walk(walker);
}

/// Look through the given type and its children to find a type for
/// which the given predicate returns true.
///
/// \param pred A predicate function object. It should return true if the
/// given type node satisfies the criteria.
///
/// \returns true if the predicate returns true for the given type or any of
/// its children.
bool findIf(llvm::function_ref<bool(TypeRepr *)> pred);

/// Check recursively whether this type repr or any of its decendants are
/// opaque return type reprs.
bool hasOpaque();

//*** Allocation Routines ************************************************/

void *operator new(size_t bytes, const ASTContext &C,
Expand Down Expand Up @@ -1108,9 +1122,9 @@ class OpaqueReturnTypeRepr : public TypeRepr {
/// A TypeRepr for a type with a generic parameter list of named opaque return
/// types.
///
/// This can occur only as the return type of a function declaration, to specify
/// subtypes which should be abstracted from callers, given a set of generic
/// constraints that the concrete types satisfy:
/// This can occur only as the return type of a function declaration, or the
/// type of a property, to specify types which should be abstracted from
/// callers, given a set of generic constraints that the concrete types satisfy:
///
/// func foo() -> <T: Collection> T { return [1] }
class NamedOpaqueReturnTypeRepr : public TypeRepr {
Expand Down
12 changes: 6 additions & 6 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5472,13 +5472,13 @@ class OpaqueTypeArchetypeType final : public ArchetypeType,
GenericEnvironment *Environment;

public:
/// Get

/// Get an opaque archetype representing the underlying type of the given
/// opaque type decl.
static OpaqueTypeArchetypeType *get(OpaqueTypeDecl *Decl,
/// opaque type decl's opaque param with ordinal `ordinal`. For example, in
/// `(some P, some Q)`, `some P`'s type param would have ordinal 0 and `some
/// Q`'s type param would have ordinal 1.
static OpaqueTypeArchetypeType *get(OpaqueTypeDecl *Decl, unsigned ordinal,
SubstitutionMap Substitutions);

OpaqueTypeDecl *getDecl() const {
return OpaqueDecl;
}
Expand Down Expand Up @@ -5509,7 +5509,7 @@ class OpaqueTypeArchetypeType final : public ArchetypeType,
///
/// then the underlying type of `some P` would be ordinal 0, and `some Q` would be ordinal 1.
unsigned getOrdinal() const {
// TODO: multiple opaque types
// TODO [OPAQUE SUPPORT]: multiple opaque types
return 0;
}

Expand Down
4 changes: 0 additions & 4 deletions include/swift/Sema/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@ enum class ConstraintKind : char {
/// The first type is a function type, the second is the function's
/// result type.
FunctionResult,
/// The first type is a type that's a candidate to be the underlying type of
/// the second opaque archetype.
OpaqueUnderlyingType,
/// The first type will be equal to the second type, but only when the
/// second type has been fully determined (and mapped down to a concrete
/// type). At that point, this constraint will be treated like an `Equal`
Expand Down Expand Up @@ -604,7 +601,6 @@ class Constraint final : public llvm::ilist_node<Constraint>,
case ConstraintKind::DynamicCallableApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::OptionalObject:
case ConstraintKind::OpaqueUnderlyingType:
case ConstraintKind::OneWayEqual:
case ConstraintKind::OneWayBindParam:
case ConstraintKind::DefaultClosureType:
Expand Down
13 changes: 13 additions & 0 deletions include/swift/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,19 @@ class LocatorPathElt::OpenedGeneric final : public StoredPointerElement<GenericS
}
};

class LocatorPathElt::OpenedOpaqueArchetype final
: public StoredPointerElement<OpaqueTypeDecl> {
public:
OpenedOpaqueArchetype(OpaqueTypeDecl *decl)
: StoredPointerElement(PathElementKind::OpenedOpaqueArchetype, decl) {}

OpaqueTypeDecl *getDecl() const { return getStoredPointer(); }

static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::OpenedOpaqueArchetype;
}
};

class LocatorPathElt::KeyPathDynamicMember final : public StoredPointerElement<NominalTypeDecl> {
public:
KeyPathDynamicMember(NominalTypeDecl *keyPathDecl)
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Sema/ConstraintLocatorPathElts.def
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ SIMPLE_LOCATOR_PATH_ELT(MemberRefBase)
/// base of the locator.
CUSTOM_LOCATOR_PATH_ELT(OpenedGeneric)

/// This is referring to a type produced by opening an opaque type archetype
/// type at the base of the locator.
CUSTOM_LOCATOR_PATH_ELT(OpenedOpaqueArchetype)

/// An optional payload.
SIMPLE_LOCATOR_PATH_ELT(OptionalPayload)

Expand Down
27 changes: 13 additions & 14 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ struct AppliedBuilderTransform {
Type builderType;

/// The result type of the body, to which the returned expression will be
/// converted.
/// converted. Opaque types should be unopened.
Type bodyResultType;

/// An expression whose value has been recorded for later use.
Expand Down Expand Up @@ -3318,9 +3318,8 @@ class ConstraintSystem {
bool isFavored = false);

/// Add the appropriate constraint for a contextual conversion.
void addContextualConversionConstraint(
Expr *expr, Type conversionType, ContextualTypePurpose purpose,
bool isOpaqueReturnType);
void addContextualConversionConstraint(Expr *expr, Type conversionType,
ContextualTypePurpose purpose);

/// Convenience function to pass an \c ArrayRef to \c addJoinConstraint
Type addJoinConstraint(ConstraintLocator *locator,
Expand Down Expand Up @@ -3856,6 +3855,12 @@ class ConstraintSystem {
/// \returns The opened type, or \c type if there are no archetypes in it.
Type openType(Type type, OpenedTypeMap &replacements);

/// "Open" an opaque archetype type.
Type openOpaqueType(Type type, ConstraintLocatorBuilder locator);

/// Recurse over the given type and open any opaque archetype types.
Type openOpaqueTypeRec(Type type, ConstraintLocatorBuilder locator);

/// "Open" the given function type.
///
/// If the function type is non-generic, this is equivalent to calling
Expand Down Expand Up @@ -4593,12 +4598,6 @@ class ConstraintSystem {
TypeMatchOptions flags,
ConstraintLocatorBuilder locator);

/// Attempt to simplify an OpaqueUnderlyingType constraint.
SolutionKind simplifyOpaqueUnderlyingTypeConstraint(Type type1,
Type type2,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator);

/// Attempt to simplify the BridgingConversion constraint.
SolutionKind simplifyBridgingConstraint(Type type1,
Type type2,
Expand Down Expand Up @@ -4729,10 +4728,10 @@ class ConstraintSystem {
///
/// \returns \c None when the result builder cannot be applied at all,
/// otherwise the result of applying the result builder.
Optional<TypeMatchResult> matchResultBuilder(
AnyFunctionRef fn, Type builderType, Type bodyResultType,
ConstraintKind bodyResultConstraintKind,
ConstraintLocatorBuilder locator);
Optional<TypeMatchResult>
matchResultBuilder(AnyFunctionRef fn, Type builderType, Type bodyResultType,
ConstraintKind bodyResultConstraintKind,
ConstraintLocatorBuilder locator);

/// Matches a wrapped or projected value parameter type to its backing
/// property wrapper type by applying the property wrapper.
Expand Down
46 changes: 23 additions & 23 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4159,9 +4159,12 @@ DependentMemberType *DependentMemberType::get(Type base,
}

OpaqueTypeArchetypeType *
OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
SubstitutionMap Substitutions)
{
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();

// 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.
Expand Down Expand Up @@ -4237,18 +4240,18 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
}
}
#else
// Assert that there are no same type constraints on the underlying type
// or its associated types.
// Assert that there are no same type constraints on the opaque type or its
// associated types.
//
// This should not be possible until we add where clause support, with the
// exception of generic base class constraints (handled below).
(void)newRequirements;
# ifndef NDEBUG
for (auto reqt :
for (auto req :
Decl->getOpaqueInterfaceGenericSignature().getRequirements()) {
auto reqtBase = reqt.getFirstType()->getRootGenericParam();
if (reqtBase->isEqual(Decl->getUnderlyingInterfaceType())) {
assert(reqt.getKind() != RequirementKind::SameType
auto reqBase = req.getFirstType()->getRootGenericParam();
if (reqBase->isEqual(opaqueParamType)) {
assert(req.getKind() != RequirementKind::SameType
&& "supporting where clauses on opaque types requires correctly "
"setting up the generic environment for "
"OpaqueTypeArchetypeTypes; see comment above");
Expand All @@ -4264,9 +4267,8 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
std::move(newRequirements)},
nullptr);

auto opaqueInterfaceTy = Decl->getUnderlyingInterfaceType();
auto layout = signature->getLayoutConstraint(opaqueInterfaceTy);
auto superclass = signature->getSuperclassBound(opaqueInterfaceTy);
auto reqs = signature->getLocalRequirements(opaqueParamType);
auto superclass = reqs.superclass;
#if !DO_IT_CORRECTLY
// Ad-hoc substitute the generic parameters of the superclass.
// If we correctly applied the substitutions to the generic signature
Expand All @@ -4275,25 +4277,23 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
superclass = superclass.subst(Substitutions);
}
#endif
const auto protos = signature->getRequiredProtocols(opaqueInterfaceTy);

auto mem = ctx.Allocate(
OpaqueTypeArchetypeType::totalSizeToAlloc<ProtocolDecl *, Type, LayoutConstraint>(
protos.size(), superclass ? 1 : 0, layout ? 1 : 0),
reqs.protos.size(), superclass ? 1 : 0, reqs.layout ? 1 : 0),
alignof(OpaqueTypeArchetypeType),
arena);

auto newOpaque = ::new (mem) OpaqueTypeArchetypeType(Decl, Substitutions,
properties,
opaqueInterfaceTy,
protos, superclass, layout);


auto newOpaque = ::new (mem)
OpaqueTypeArchetypeType(Decl, Substitutions, properties, opaqueParamType,
reqs.protos, superclass, reqs.layout);

// Create a generic environment and bind the opaque archetype to the
// opaque interface type from the decl's signature.
auto *env = GenericEnvironment::getIncomplete(signature);
env->addMapping(GenericParamKey(opaqueInterfaceTy), newOpaque);
env->addMapping(GenericParamKey(opaqueParamType), newOpaque);
newOpaque->Environment = env;

// Look up the insertion point in the folding set again in case something
// invalidated it above.
{
Expand All @@ -4303,7 +4303,7 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
assert(!existing && "race to create opaque archetype?!");
set.InsertNode(newOpaque, insertPos);
}

return newOpaque;
}

Expand Down