Skip to content

Commit

Permalink
Merge pull request #8065 from slavapestov/generic-typealias-missing-c…
Browse files Browse the repository at this point in the history
…heck

Sema: Actually check generic typealias requirements
  • Loading branch information
slavapestov committed Mar 14, 2017
2 parents 15431e9 + 4a46023 commit 1bbb941
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 61 deletions.
113 changes: 52 additions & 61 deletions lib/Sema/TypeCheckType.cpp
Expand Up @@ -615,88 +615,79 @@ Type TypeChecker::applyUnboundGenericArguments(
if (!resolver)
resolver = &defaultResolver;

// Validate the generic arguments and capture just the types.
SmallVector<Type, 4> genericArgTypes;
for (auto &genericArg : genericArgs) {
// Validate the generic argument.
if (validateType(genericArg, dc, options, resolver, unsatisfiedDependency))
return ErrorType::get(Context);

if (!genericArg.getType())
return nullptr;
auto genericSig = decl->getGenericSignature();
assert(genericSig != nullptr);

genericArgTypes.push_back(genericArg.getType());
}
TypeSubstitutionMap subs;

// If we're completing a generic TypeAlias, then we map the types provided
// onto the underlying type.
if (auto *TAD = dyn_cast<TypeAliasDecl>(decl)) {
TypeSubstitutionMap subs;
// Get the substitutions for outer generic parameters from the parent
// type.
auto *unboundType = type->castTo<UnboundGenericType>();
if (auto parentType = unboundType->getParent())
subs = parentType->getContextSubstitutions(decl->getDeclContext());

// The type should look like SomeNominal<T, U>.Alias<V, W>.
SourceLoc noteLoc = decl->getLoc();
if (noteLoc.isInvalid())
noteLoc = loc;

// Get the substitutions for outer generic parameters from the parent
// type.
auto *unboundType = type->castTo<UnboundGenericType>();
if (auto parentType = unboundType->getParent())
subs = parentType->getContextSubstitutions(TAD->getDeclContext());
// Realize the types of the generic arguments and add them to the
// substitution map.
bool hasTypeParameterOrVariable = false;
for (unsigned i = 0, e = genericArgs.size(); i < e; i++) {
auto &genericArg = genericArgs[i];

// Get the substitutions for the inner parameters.
auto signature = TAD->getGenericSignature();
for (unsigned i = 0, e = genericArgs.size(); i < e; i++) {
auto t = signature->getInnermostGenericParams()[i];
subs[t->getCanonicalType()->castTo<GenericTypeParamType>()] =
genericArgs[i].getType();
}

// Apply substitutions to the interface type of the typealias.
type = TAD->getDeclaredInterfaceType();
return type.subst(QueryTypeSubstitutionMap{subs},
LookUpConformanceInModule(dc->getParentModule()),
SubstFlags::UseErrorType);
}

// Form the bound generic type.
auto *UGT = type->castTo<UnboundGenericType>();
auto *BGT = BoundGenericType::get(cast<NominalTypeDecl>(decl),
UGT->getParent(), genericArgTypes);
// Propagate failure.
if (validateType(genericArg, dc, options, resolver, unsatisfiedDependency))
return ErrorType::get(Context);

// Check protocol conformance.
if (!BGT->hasTypeParameter() && !BGT->hasTypeVariable()) {
SourceLoc noteLoc = decl->getLoc();
if (noteLoc.isInvalid())
noteLoc = loc;
auto origTy = genericSig->getInnermostGenericParams()[i];
auto substTy = genericArg.getType();

// FIXME: Record that we're checking substitutions, so we can't end up
// with infinite recursion.
// Unsatisfied dependency case.
if (!substTy)
return nullptr;

// Check the generic arguments against the generic signature.
auto genericSig = decl->getGenericSignature();
// Enter a substitution.
subs[origTy->getCanonicalType()->castTo<GenericTypeParamType>()] =
substTy;

// Collect the complete set of generic arguments.
assert(genericSig != nullptr);
auto substitutions = BGT->getContextSubstitutions(BGT->getDecl());
hasTypeParameterOrVariable |=
(substTy->hasTypeParameter() || substTy->hasTypeVariable());
}

// Check the generic arguments against the requirements of the declaration's
// generic signature.
if (!hasTypeParameterOrVariable) {
auto result =
checkGenericArguments(dc, loc, noteLoc, UGT, genericSig,
QueryTypeSubstitutionMap{substitutions},
LookUpConformanceInModule{dc->getParentModule()},
unsatisfiedDependency);
checkGenericArguments(dc, loc, noteLoc, unboundType, genericSig,
QueryTypeSubstitutionMap{subs},
LookUpConformanceInModule{dc->getParentModule()},
unsatisfiedDependency);

switch (result) {
case RequirementCheckResult::UnsatisfiedDependency:
return Type();
case RequirementCheckResult::Failure:
return ErrorType::get(Context);

case RequirementCheckResult::Success:
if (useObjectiveCBridgeableConformancesOfArgs(dc, BGT,
unsatisfiedDependency))
return Type();
break;
}
}

return BGT;
// Apply the substitution map to the interface type of the declaration.
type = decl->getDeclaredInterfaceType();
type = type.subst(QueryTypeSubstitutionMap{subs},
LookUpConformanceInModule(dc->getParentModule()),
SubstFlags::UseErrorType);

if (isa<NominalTypeDecl>(decl)) {
if (useObjectiveCBridgeableConformancesOfArgs(
dc, type->castTo<BoundGenericType>(),
unsatisfiedDependency))
return Type();
}

return type;
}

/// \brief Diagnose a use of an unbound generic type.
Expand Down
13 changes: 13 additions & 0 deletions test/decl/typealias/generic.swift
Expand Up @@ -340,3 +340,16 @@ func f(x: S.G1<Int>, y: S.G2<Int>) {
takesMyType(x: x)
takesMyType(y: y)
}

//
// Generic typealiases with requirements
//

typealias Element<S> = S.Iterator.Element where S : Sequence

func takesInt(_: Element<[Int]>) {}

takesInt(10)

func failsRequirementCheck(_: Element<Int>) {}
// expected-error@-1 {{type 'Int' does not conform to protocol 'Sequence'}}

0 comments on commit 1bbb941

Please sign in to comment.