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

Concrete constrained extensions #5126

Merged
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
5 changes: 4 additions & 1 deletion include/swift/AST/ArchetypeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,11 @@ class ArchetypeBuilder {
/// Finalize the set of requirements, performing any remaining checking
/// required before generating archetypes.
///
/// \param allowConcreteGenericParams If true, allow generic parameters to
/// be made concrete.
///
/// \returns true if an error occurs, false otherwise.
bool finalize(SourceLoc loc);
bool finalize(SourceLoc loc, bool allowConcreteGenericParams=false);

/// \brief Resolve the given type to the potential archetype it names.
///
Expand Down
80 changes: 60 additions & 20 deletions lib/AST/ArchetypeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1155,15 +1155,6 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes(
compareDependentTypes(&T2, &T1) < 0))
std::swap(T1, T2);

// Don't allow two generic parameters to be equivalent, because then we
// don't actually have two parameters.
// FIXME: Should we simply allow this?
if (T1Depth == 0 && T2Depth == 0) {
Diags.diagnose(Source.getLoc(), diag::requires_generic_params_made_equal,
T1->getName(), T2->getName());
return true;
}

// Merge any concrete constraints.
Type concrete1 = T1->ArchetypeOrConcreteType.getAsConcreteType();
Type concrete2 = T2->ArchetypeOrConcreteType.getAsConcreteType();
Expand Down Expand Up @@ -1248,16 +1239,6 @@ bool ArchetypeBuilder::addSameTypeRequirementToConcrete(
return false;
}

// Don't allow a generic parameter to be equivalent to a concrete type,
// because then we don't actually have a parameter.
// FIXME: Should we simply allow this?
if (T->getNestingDepth() == 0) {
Diags.diagnose(Source.getLoc(),
diag::requires_generic_param_made_equal_to_concrete,
T->getName());
return true;
}

// Make sure the concrete type fulfills the requirements on the archetype.
DenseMap<ProtocolDecl *, ProtocolConformance*> conformances;
if (!Concrete->is<ArchetypeType>()) {
Expand Down Expand Up @@ -1752,8 +1733,67 @@ static Identifier typoCorrectNestedType(
return bestMatches.front();
}

bool ArchetypeBuilder::finalize(SourceLoc loc) {
bool
ArchetypeBuilder::finalize(SourceLoc loc, bool allowConcreteGenericParams) {
bool invalid = false;
SmallPtrSet<PotentialArchetype *, 4> visited;

// Check for generic parameters which have been made concrete or equated
// with each other.
if (!allowConcreteGenericParams) {
unsigned depth = 0;
for (const auto &pair : Impl->PotentialArchetypes) {
depth = std::max(depth, pair.second->getRootParam()->getDepth());
}

for (const auto &pair : Impl->PotentialArchetypes) {
auto pa = pair.second;
auto rep = pa->getRepresentative();

if (pa->getRootParam()->getDepth() < depth)
continue;

if (!visited.insert(rep).second)
continue;

// Don't allow a generic parameter to be equivalent to a concrete type,
// because then we don't actually have a parameter.
if (rep->ArchetypeOrConcreteType.getAsConcreteType()) {
auto &Source = rep->SameTypeSource;

// For auto-generated locations, we should have diagnosed the problem
// elsewhere already.
if (!Source->getLoc().isValid())
continue;

Diags.diagnose(Source->getLoc(),
diag::requires_generic_param_made_equal_to_concrete,
rep->getName());
invalid = true;
continue;
}

// Don't allow two generic parameters to be equivalent, because then we
// don't actually have two parameters.
for (auto other : rep->getEquivalenceClass()) {
if (pa != other && other->getParent() == nullptr) {
auto &Source = (other == rep ? pa->SameTypeSource
: other->SameTypeSource);

// For auto-generated locations, we should have diagnosed the problem
// elsewhere already.
if (!Source->getLoc().isValid())
continue;

Diags.diagnose(Source->getLoc(),
diag::requires_generic_params_made_equal,
pa->getName(), other->getName());
invalid = true;
break;
}
}
}
}

// If any nested types remain unresolved, produce diagnostics.
if (Impl->NumUnresolvedNestedTypes > 0) {
Expand Down
3 changes: 2 additions & 1 deletion lib/SIL/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,8 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
unsigned disambiguatedNameCounter = 1;
for (auto *paramTy : sig->getGenericParams()) {
auto *archetypeTy = mapTypeIntoContext(paramTy)->getAs<ArchetypeType>();
assert(archetypeTy);
if (!archetypeTy)
continue;

Identifier name = archetypeTy->getName();
while (!UsedNames.insert(name).second) {
Expand Down
31 changes: 24 additions & 7 deletions lib/SIL/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1678,11 +1678,10 @@ TypeConverter::getEffectiveGenericEnvironment(AnyFunctionRef fn,
CaptureInfo captureInfo) {
auto dc = fn.getAsDeclContext();

if (dc->getParent()->isLocalContext() &&
!captureInfo.hasGenericParamCaptures())
return nullptr;
if (getEffectiveGenericSignature(fn, captureInfo))
return dc->getGenericEnvironmentOfContext();

return dc->getGenericEnvironmentOfContext();
return nullptr;
}

CanGenericSignature
Expand All @@ -1694,8 +1693,11 @@ TypeConverter::getEffectiveGenericSignature(AnyFunctionRef fn,
!captureInfo.hasGenericParamCaptures())
return nullptr;

if (auto sig = dc->getGenericSignatureOfContext())
if (auto sig = dc->getGenericSignatureOfContext()) {
if (sig->areAllParamsConcrete())
return nullptr;
return sig->getCanonicalSignature();
}

return nullptr;
}
Expand Down Expand Up @@ -1990,18 +1992,33 @@ getMaterializeForSetCallbackType(AbstractStorageDecl *storage,
}
}

CanType canSelfType;
CanType canSelfMetatypeType;
if (genericSig) {
canSelfType = genericSig->getCanonicalTypeInContext(
selfType, *M.getSwiftModule());
canSelfMetatypeType = genericSig->getCanonicalTypeInContext(
selfMetatypeType, *M.getSwiftModule());
} else {
canSelfType = selfType->getCanonicalType();
canSelfMetatypeType = selfMetatypeType->getCanonicalType();
}

// Create the SILFunctionType for the callback.
SILParameterInfo params[] = {
{ ctx.TheRawPointerType, ParameterConvention::Direct_Unowned },
{ ctx.TheUnsafeValueBufferType, ParameterConvention::Indirect_Inout },
{ selfType->getCanonicalType(), ParameterConvention::Indirect_Inout },
{ selfMetatypeType->getCanonicalType(), ParameterConvention::Direct_Unowned },
{ canSelfType, ParameterConvention::Indirect_Inout },
{ canSelfMetatypeType, ParameterConvention::Direct_Unowned },
};
ArrayRef<SILResultInfo> results = {};
auto extInfo =
SILFunctionType::ExtInfo()
.withRepresentation(SILFunctionTypeRepresentation::Thin);

if (genericSig && genericSig->areAllParamsConcrete())
genericSig = nullptr;

return SILFunctionType::get(genericSig, extInfo,
/*callee*/ ParameterConvention::Direct_Unowned,
params, results, None, ctx);
Expand Down
17 changes: 8 additions & 9 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,20 +1044,19 @@ void ConstraintSystem::openGeneric(
ConstraintLocatorBuilder locator,
llvm::DenseMap<CanType, TypeVariableType *> &replacements) {
auto locatorPtr = getConstraintLocator(locator);

auto *genericEnv = innerDC->getGenericEnvironmentOfContext();

// Create the type variables for the generic parameters.
for (auto gp : params) {
auto contextTy = genericEnv->mapTypeIntoContext(gp);
if (auto *archetype = contextTy->getAs<ArchetypeType>()) {
auto typeVar = createTypeVariable(getConstraintLocator(
locator.withPathElement(
LocatorPathElt(archetype))),
TVO_PrefersSubtypeBinding |
TVO_MustBeMaterializable);
replacements[gp->getCanonicalType()] = typeVar;
}
if (auto *archetype = contextTy->getAs<ArchetypeType>())
locatorPtr = getConstraintLocator(
locator.withPathElement(LocatorPathElt(archetype)));

auto typeVar = createTypeVariable(locatorPtr,
TVO_PrefersSubtypeBinding |
TVO_MustBeMaterializable);
replacements[gp->getCanonicalType()] = typeVar;
}

GetTypeVariable getTypeVariable{*this, locator};
Expand Down
10 changes: 5 additions & 5 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ TypeChecker::handleSILGenericParams(GenericParamList *genericParams,
auto genericParams = nestedList.rbegin()[i];
bool invalid = false;
auto *genericSig = validateGenericSignature(genericParams, DC, parentSig,
/*allowConcreteGenericParams=*/true,
nullptr, invalid);
if (invalid)
return std::make_pair(nullptr, nullptr);
Expand Down Expand Up @@ -7545,11 +7546,10 @@ static Type checkExtensionGenericParams(
bool invalid = false;
auto *parentSig = ext->getDeclContext()->getGenericSignatureOfContext();
auto *parentEnv = ext->getDeclContext()->getGenericEnvironmentOfContext();
GenericSignature *sig = tc.validateGenericSignature(
genericParams,
ext->getDeclContext(),
parentSig,
inferExtendedTypeReqs, invalid);
auto *sig = tc.validateGenericSignature(genericParams,
ext->getDeclContext(), parentSig,
/*allowConcreteGenericParams=*/true,
inferExtendedTypeReqs, invalid);
ext->setGenericSignature(sig);

if (invalid) {
Expand Down
5 changes: 4 additions & 1 deletion lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ GenericSignature *TypeChecker::validateGenericSignature(
GenericParamList *genericParams,
DeclContext *dc,
GenericSignature *parentSig,
bool allowConcreteGenericParams,
std::function<bool(ArchetypeBuilder &)> inferRequirements,
bool &invalid) {
assert(genericParams && "Missing generic parameters?");
Expand All @@ -747,7 +748,8 @@ GenericSignature *TypeChecker::validateGenericSignature(
}

// Finalize the generic requirements.
(void)builder.finalize(genericParams->getSourceRange().Start);
(void)builder.finalize(genericParams->getSourceRange().Start,
allowConcreteGenericParams);

// The archetype builder now has all of the requirements, although there might
// still be errors that have not yet been diagnosed. Revert the signature
Expand Down Expand Up @@ -920,6 +922,7 @@ bool TypeChecker::validateGenericTypeSignature(GenericTypeDecl *typeDecl) {
}

auto *sig = validateGenericSignature(gp, dc, dc->getGenericSignatureOfContext(),
/*allowConcreteGenericParams=*/false,
nullptr, invalid);
assert(sig->getInnermostGenericParams().size()
== typeDecl->getGenericParams()->size());
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,7 @@ class TypeChecker final : public LazyResolver {
GenericParamList *genericParams,
DeclContext *dc,
GenericSignature *outerSignature,
bool allowConcreteGenericParams,
std::function<bool(ArchetypeBuilder &)> inferRequirements,
bool &invalid);

Expand Down
65 changes: 64 additions & 1 deletion test/Constraints/same_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func testAssocTypeEquivalence<T: Fooable>(_ fooable: T) -> X.Type
}

func fail6<T>(_ t: T) -> Int where T == Int { // expected-error{{same-type requirement makes generic parameter 'T' non-generic}}
return t // expected-error{{cannot convert return expression of type 'T' to return type 'Int'}}
return t
}

func test8<T: Barrable, U: Barrable>(_ t: T, u: U) -> (Y, Y, X, X)
Expand Down Expand Up @@ -142,3 +142,66 @@ struct BadFooable : Fooable {
func bogusInOutError(d: inout Brunch<BadFooable>) {}
// expected-error@-1{{'Brunch' requires the types '<<error type>>' and 'X' be equivalent}}

// Some interesting invalid cases that used to crash
protocol P {
associatedtype A
associatedtype B
}

struct Q : P {
typealias A = Int
typealias B = Int
}

struct S1<T : P> {
func foo<X, Y>(x: X, y: Y) where X == T.A, Y == T.B {
print(X.self)
print(Y.self)
print(x)
print(y)
}
}
S1<Q>().foo(x: 1, y: 2)

struct S2<T : P> where T.A == T.B {
// expected-error@+1 {{same-type requirement makes generic parameters 'X' and 'Y' equivalent}}
func foo<X, Y>(x: X, y: Y) where X == T.A, Y == T.B {
print(X.self)
print(Y.self)
print(x)
print(y)
}
}
S2<Q>().foo(x: 1, y: 2)

struct S3<T : P> {
// expected-error@+1 {{same-type requirement makes generic parameters 'X' and 'Y' equivalent}}
func foo<X, Y>(x: X, y: Y) where X == T.A, Y == T.A {}
}
S3<Q>().foo(x: 1, y: 2)

// Secondaries can be equated OK, even if we're imposing
// new conformances onto an outer secondary

protocol PPP {}

protocol PP {
associatedtype A : PPP
}

struct SSS : PPP {}
struct SS : PP { typealias A = SSS }

struct QQ : P {
typealias A = SSS
typealias B = Int
}

struct S4<T : P> {
func foo<X : PP>(x: X) where X.A == T.A {
print(x)
print(X.self)
}
}

S4<QQ>().foo(x: SS())
2 changes: 1 addition & 1 deletion test/Generics/function_defs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,5 +294,5 @@ func badTypeConformance1<T>(_: T) where Int : EqualComparable {} // expected-err

func badTypeConformance2<T>(_: T) where T.Blarg : EqualComparable { } // expected-error{{'Blarg' is not a member type of 'T'}}

func badSameType<T, U : GeneratesAnElement, V>(_ : T) // expected-error{{generic parameter 'V' is not used in function signature}}
func badSameType<T, U : GeneratesAnElement, V>(_ : T)
where T == U.Element, U.Element == V {} // expected-error{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
Loading