Skip to content

Sema: Arrange TypeCheckType to open unbound generics on the fly #32116

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 1 commit into from
Jul 8, 2020
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
4 changes: 2 additions & 2 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -2444,7 +2444,7 @@ class HasImplementationOnlyImportsRequest

class ResolveTypeRequest
: public SimpleRequest<ResolveTypeRequest,
Type(TypeResolution *, TypeRepr *),
Type(const TypeResolution *, TypeRepr *),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -2457,7 +2457,7 @@ class ResolveTypeRequest
friend SimpleRequest;

// Evaluation.
Type evaluate(Evaluator &evaluator, TypeResolution *resolution,
Type evaluate(Evaluator &evaluator, const TypeResolution *resolution,
TypeRepr *repr) const;
};

Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ SWIFT_REQUEST(TypeChecker, ResolveTypeEraserTypeRequest,
Type(ProtocolDecl *, TypeEraserAttr *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ResolveTypeRequest,
Type (TypeResolution *, TypeRepr *), Uncached, NoLocationInfo)
Type (const TypeResolution *, TypeRepr *),
Uncached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SPIGroupsRequest,
llvm::ArrayRef<Identifier>(Decl *),
Cached, NoLocationInfo)
Expand Down
137 changes: 81 additions & 56 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1445,12 +1445,19 @@ namespace {
diag::super_with_no_base_class);
}

Type resolveTypeReferenceInExpression(TypeRepr *repr,
TypeResolverContext resCtx) {
TypeResolutionOptions options(resCtx);
options |= TypeResolutionFlags::AllowUnboundGenerics;
auto result = TypeResolution::forContextual(CS.DC, options)
.resolveType(repr);
Type
resolveTypeReferenceInExpression(TypeRepr *repr, TypeResolverContext resCtx,
OpenUnboundGenericTypeFn unboundTyOpener) {
if (!unboundTyOpener) {
unboundTyOpener = [](auto unboundTy) {
// FIXME: Don't let unbound generic types escape type resolution.
// For now, just return the unbound generic type.
return unboundTy;
};
}
const auto result =
TypeResolution::forContextual(CS.DC, resCtx, unboundTyOpener)
.resolveType(repr);
if (result->hasError()) {
return Type();
}
Expand All @@ -1460,20 +1467,21 @@ namespace {
Type visitTypeExpr(TypeExpr *E) {
Type type;
// If this is an implicit TypeExpr, don't validate its contents.
auto *const locator = CS.getConstraintLocator(E);
if (E->isImplicit()) {
type = CS.getInstanceType(CS.cacheType(E));
assert(type && "Implicit type expr must have type set!");
type = CS.openUnboundGenericTypes(type, locator);
} else {
auto *repr = E->getTypeRepr();
assert(repr && "Explicit node has no type repr!");
type = resolveTypeReferenceInExpression(
repr, TypeResolverContext::InExpression);
repr, TypeResolverContext::InExpression,
OpenUnboundGenericType(CS, locator));
}

if (!type || type->hasError()) return Type();

auto locator = CS.getConstraintLocator(E);
type = CS.openUnboundGenericTypes(type, locator);

return MetatypeType::get(type);
}

Expand Down Expand Up @@ -1711,12 +1719,15 @@ namespace {

// Bind the specified generic arguments to the type variables in the
// open type.
auto locator = CS.getConstraintLocator(expr);
auto *const locator = CS.getConstraintLocator(expr);
const auto options =
TypeResolutionOptions(TypeResolverContext::InExpression);
for (size_t i = 0, size = specializations.size(); i < size; ++i) {
TypeResolutionOptions options(TypeResolverContext::InExpression);
options |= TypeResolutionFlags::AllowUnboundGenerics;
auto result = TypeResolution::forContextual(CS.DC, options)
.resolveType(specializations[i]);
const auto resolution = TypeResolution::forContextual(
CS.DC, options,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, locator));
const auto result = resolution.resolveType(specializations[i]);
if (result->hasError())
return Type();

Expand Down Expand Up @@ -2190,7 +2201,7 @@ namespace {

return resolveTypeReferenceInExpression(
closure->getExplicitResultTypeRepr(),
TypeResolverContext::InExpression);
TypeResolverContext::InExpression, nullptr);
};

Type resultTy;
Expand Down Expand Up @@ -2493,17 +2504,12 @@ namespace {
case PatternKind::Is: {
auto isPattern = cast<IsPattern>(pattern);

Type castType = resolveTypeReferenceInExpression(
isPattern->getCastTypeRepr(), TypeResolverContext::InExpression);

if (!castType)
return Type();

castType = CS.openUnboundGenericTypes(
castType,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));

assert(castType);
const Type castType = resolveTypeReferenceInExpression(
isPattern->getCastTypeRepr(), TypeResolverContext::InExpression,
OpenUnboundGenericType(CS,
locator.withPathElement(
LocatorPathElt::PatternMatch(pattern))));
if (!castType) return Type();

auto *subPattern = isPattern->getSubPattern();
Type subPatternType = getTypeForPattern(
Expand Down Expand Up @@ -2559,7 +2565,14 @@ namespace {
}
return resolveTypeReferenceInExpression(
enumPattern->getParentTypeRepr(),
TypeResolverContext::InExpression);
TypeResolverContext::InExpression, [](auto unboundTy) {
// FIXME: We ought to pass an OpenUnboundGenericType object
// rather than calling CS.openUnboundGenericType below, but
// sometimes the parent type is resolved eagerly in
// ResolvePattern::visitUnresolvedDotExpr, letting unbound
// generics escape.
return unboundTy;
});
}();

if (!parentType)
Expand Down Expand Up @@ -2725,9 +2738,10 @@ namespace {
// of is-patterns applied to an irrefutable pattern.
pattern = pattern->getSemanticsProvidingPattern();
while (auto isp = dyn_cast<IsPattern>(pattern)) {
Type castType = TypeResolution::forContextual(
CS.DC, TypeResolverContext::InExpression)
.resolveType(isp->getCastTypeRepr());
const Type castType = TypeResolution::forContextual(
CS.DC, TypeResolverContext::InExpression,
/*unboundTyOpener*/ nullptr)
.resolveType(isp->getCastTypeRepr());
if (castType->hasError()) {
return false;
}
Expand Down Expand Up @@ -3051,14 +3065,14 @@ namespace {

auto *const repr = expr->getCastTypeRepr();
// Validate the resulting type.
const auto type = resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr);
if (!type)
const auto toType = resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, CS.getConstraintLocator(expr)));
if (!toType)
return nullptr;

// Open the type we're casting to.
const auto toType =
CS.openUnboundGenericTypes(type, CS.getConstraintLocator(expr));
// Cache the type we're casting to.
if (repr) CS.setType(repr, toType);

auto fromType = CS.getType(fromExpr);
Expand All @@ -3079,7 +3093,14 @@ namespace {
// Validate the resulting type.
auto *const repr = expr->getCastTypeRepr();
const auto type = resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr);
repr, TypeResolverContext::ExplicitCastExpr, [](auto unboundTy) {
// FIXME: We ought to pass an OpenUnboundGenericType object rather
// than calling CS.openUnboundGenericType after resolving, but
// sometimes the type expression is resolved eagerly in
// PreCheckExpression::simplifyTypeConstructionWithLiteralArg,
// letting unbound generics escape.
return unboundTy;
});
if (!type)
return nullptr;

Expand Down Expand Up @@ -3111,14 +3132,14 @@ namespace {

// Validate the resulting type.
auto *const repr = expr->getCastTypeRepr();
const auto type = resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr);
if (!type)
const auto toType = resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, CS.getConstraintLocator(expr)));
if (!toType)
return nullptr;

// Open the type we're casting to.
const auto toType =
CS.openUnboundGenericTypes(type, CS.getConstraintLocator(expr));
// Cache the type we're casting to.
if (repr) CS.setType(repr, toType);

auto fromType = CS.getType(fromExpr);
Expand All @@ -3137,17 +3158,16 @@ namespace {

Type visitIsExpr(IsExpr *expr) {
// Validate the type.
// FIXME: Locator for the cast type?
auto &ctx = CS.getASTContext();
const auto type = resolveTypeReferenceInExpression(
expr->getCastTypeRepr(),
TypeResolverContext::ExplicitCastExpr);
if (!type)
const auto toType = resolveTypeReferenceInExpression(
expr->getCastTypeRepr(), TypeResolverContext::ExplicitCastExpr,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, CS.getConstraintLocator(expr)));
if (!toType)
return nullptr;

// Open up the type we're checking.
// FIXME: Locator for the cast type?
const auto toType =
CS.openUnboundGenericTypes(type, CS.getConstraintLocator(expr));
// Cache the type we're checking.
CS.setType(expr->getCastTypeRepr(), toType);

// Add a checked cast constraint.
Expand Down Expand Up @@ -3353,7 +3373,7 @@ namespace {
// Just resolve the referenced type.
// FIXME: The type reference needs to be opened into context.
return resolveTypeReferenceInExpression(
placeholderRepr, TypeResolverContext::InExpression);
placeholderRepr, TypeResolverContext::InExpression, nullptr);
}

auto locator = CS.getConstraintLocator(E);
Expand Down Expand Up @@ -3423,11 +3443,13 @@ namespace {

// If a root type was explicitly given, then resolve it now.
if (auto rootRepr = E->getRootType()) {
auto rootObjectTy = resolveTypeReferenceInExpression(
rootRepr, TypeResolverContext::InExpression);
const auto rootObjectTy = resolveTypeReferenceInExpression(
rootRepr, TypeResolverContext::InExpression,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, locator));
if (!rootObjectTy || rootObjectTy->hasError())
return Type();
rootObjectTy = CS.openUnboundGenericTypes(rootObjectTy, locator);

// Allow \Derived.property to be inferred as \Base.property to
// simulate a sort of covariant conversion from
// KeyPath<Derived, T> to KeyPath<Base, T>.
Expand Down Expand Up @@ -3887,6 +3909,9 @@ static Type generateWrappedPropertyTypeConstraints(

auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers();
for (unsigned i : indices(wrapperAttributes)) {
// FIXME: We should somehow pass an OpenUnboundGenericTypeFn to
// AttachedPropertyWrapperTypeRequest::evaluate to open up unbound
// generics on the fly.
Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i);
auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i);
if (rawWrapperType->hasError() || !wrapperInfo)
Expand Down
10 changes: 8 additions & 2 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,8 +997,14 @@ void ConstraintSystem::shrink(Expr *expr) {
auto *const typeRepr = coerceExpr->getCastTypeRepr();

if (typeRepr && isSuitableCollection(typeRepr)) {
auto resolution = TypeResolution::forContextual(CS.DC, None);
auto coercionType = resolution.resolveType(typeRepr);
const auto coercionType =
TypeResolution::forContextual(
CS.DC, None,
// FIXME: Should we really be unconditionally complaining
// about unbound generics here? For example:
// let foo: [Array<Float>] = [[0], [1], [2]] as [Array]
/*unboundTyOpener*/ nullptr)
.resolveType(typeRepr);

// Looks like coercion type is invalid, let's skip this sub-tree.
if (coercionType->hasError())
Expand Down
6 changes: 4 additions & 2 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,8 @@ Type ConstraintSystem::openUnboundGenericType(
// handle generic TypeAliases elsewhere, this can just become a
// call to BoundGenericType::get().
return TypeChecker::applyUnboundGenericArguments(
decl, parentTy, SourceLoc(), TypeResolution::forContextual(DC, None),
decl, parentTy, SourceLoc(),
TypeResolution::forContextual(DC, None, /*unboundTyOpener*/ nullptr),
arguments);
}

Expand Down Expand Up @@ -1217,7 +1218,8 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
// Resolve the reference to this type declaration in our current context.
auto type = TypeChecker::resolveTypeInContext(
typeDecl, nullptr,
TypeResolution::forContextual(useDC, TypeResolverContext::InExpression),
TypeResolution::forContextual(useDC, TypeResolverContext::InExpression,
/*unboundTyOpener*/ nullptr),
/*isSpecialized=*/false);

checkNestedTypeConstraints(*this, type, locator);
Expand Down
19 changes: 19 additions & 0 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -5027,6 +5027,25 @@ class ConstraintSystem {
void print(raw_ostream &out, Expr *) const;
};

/// A function object suitable for use as an \c OpenUnboundGenericTypeFn that
/// "opens" the given unbound type by introducing fresh type variables for
/// generic parameters and constructing a bound generic type from these
/// type variables.
class OpenUnboundGenericType {
ConstraintSystem &cs;
const ConstraintLocatorBuilder &locator;

public:
explicit OpenUnboundGenericType(ConstraintSystem &cs,
const ConstraintLocatorBuilder &locator)
: cs(cs), locator(locator) {}

Type operator()(UnboundGenericType *unboundTy) const {
return cs.openUnboundGenericType(unboundTy->getDecl(),
unboundTy->getParent(), locator);
}
};

/// Compute the shuffle required to map from a given tuple type to
/// another tuple type.
///
Expand Down
Loading