Skip to content

Commit 08bb001

Browse files
committed
AST: Add GenericSignatureImpl::getReducedTypeParameter()
This avoids a bit of indirection when the input is already known to be a type parameter, and not just a type that contains type parameters.
1 parent be9d999 commit 08bb001

File tree

5 files changed

+128
-112
lines changed

5 files changed

+128
-112
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,11 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
404404

405405
bool isReducedType(Type type) const;
406406

407+
/// Return the reduced version of the given type parameter under this generic
408+
/// signature. To reduce a type that more generally contains type parameters,
409+
/// use GenericSignature::getReducedType().
410+
CanType getReducedTypeParameter(CanType type) const;
411+
407412
/// Determine whether the given type parameter is defined under this generic
408413
/// signature.
409414
bool isValidTypeParameter(Type type) const;

lib/AST/GenericEnvironment.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ GenericEnvironment::getOrCreateArchetypeFromInterfaceType(Type depType) {
378378
auto genericSig = getGenericSignature();
379379

380380
// Reduce it.
381-
auto reducedType = genericSig.getReducedType(canType);
381+
auto reducedType = genericSig->getReducedTypeParameter(canType);
382382

383383
// If this type parameter is equivalent to a concrete type,
384384
// map the concrete type into context and cache the result.

lib/AST/GenericSignature.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ CanType GenericSignatureImpl::getReducedType(Type type) const {
504504
type, { })->getCanonicalType();
505505
}
506506

507+
CanType GenericSignatureImpl::getReducedTypeParameter(CanType type) const {
508+
return getRequirementMachine()->getReducedTypeParameter(
509+
type, { })->getCanonicalType();
510+
}
511+
507512
bool GenericSignatureImpl::isValidTypeParameter(Type type) const {
508513
return getRequirementMachine()->isValidTypeParameter(type);
509514
}

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 115 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,120 @@ static Type substPrefixType(Type type, unsigned suffixLength, Type prefixType,
345345
std::nullopt);
346346
}
347347

348+
Type RequirementMachine::getReducedTypeParameter(
349+
CanType t,
350+
ArrayRef<GenericTypeParamType *> genericParams) const {
351+
// Get a simplified term T.
352+
auto term = Context.getMutableTermForType(t, /*proto=*/nullptr);
353+
System.simplify(term);
354+
355+
// We need to handle "purely concrete" member types, eg if I have a
356+
// signature <T where T == Foo>, and we're asked to reduce the
357+
// type T.[P:A] where Foo : A.
358+
//
359+
// This comes up because we can derive the signature <T where T == Foo>
360+
// from a generic signature like <T where T : P>; adding the
361+
// concrete requirement 'T == Foo' renders 'T : P' redundant. We then
362+
// want to take interface types written against the original signature
363+
// and reduce them with respect to the derived signature.
364+
//
365+
// The problem is that T.[P:A] is not a valid term in the rewrite system
366+
// for <T where T == Foo>, since we do not have the requirement T : P.
367+
//
368+
// A more principled solution would build a substitution map when
369+
// building a derived generic signature that adds new requirements;
370+
// interface types would first be substituted before being reduced
371+
// in the new signature.
372+
//
373+
// For now, we handle this with a two-step process; we split a term up
374+
// into a longest valid prefix, which must resolve to a concrete type,
375+
// and the remaining suffix, which we use to perform a concrete
376+
// substitution using subst().
377+
378+
// In the below, let T be a type term, with T == UV, where U is the
379+
// longest valid prefix.
380+
//
381+
// Note that V can be empty if T is fully valid; we expect this to be
382+
// true most of the time.
383+
auto prefix = getLongestValidPrefix(term);
384+
385+
// Get a type (concrete or dependent) for U.
386+
auto prefixType = [&]() -> Type {
387+
if (prefix.empty())
388+
return Type();
389+
390+
verify(prefix);
391+
392+
auto *props = Map.lookUpProperties(prefix);
393+
if (props) {
394+
if (props->isConcreteType()) {
395+
auto concreteType = props->getConcreteType(genericParams,
396+
prefix, Map);
397+
if (!concreteType->hasTypeParameter())
398+
return concreteType;
399+
400+
// FIXME: Recursion guard is needed here
401+
return getReducedType(concreteType, genericParams);
402+
}
403+
404+
// Skip this part if the entire input term is valid, because in that
405+
// case we don't want to replace the term with its superclass bound;
406+
// unlike a fixed concrete type, the superclass bound only comes into
407+
// play when looking up a member type.
408+
if (props->hasSuperclassBound() &&
409+
prefix.size() != term.size()) {
410+
auto superclass = props->getSuperclassBound(genericParams,
411+
prefix, Map);
412+
if (!superclass->hasTypeParameter())
413+
return superclass;
414+
415+
// FIXME: Recursion guard is needed here
416+
return getReducedType(superclass, genericParams);
417+
}
418+
}
419+
420+
return Map.getTypeForTerm(prefix, genericParams);
421+
}();
422+
423+
// If T is already valid, the longest valid prefix U of T is T itself, and
424+
// V is empty. Just return the type we computed above.
425+
//
426+
// This is the only case where U is allowed to be dependent.
427+
if (prefix.size() == term.size())
428+
return prefixType;
429+
430+
// If U is not concrete, we have an invalid member type of a dependent
431+
// type, which is not valid in this generic signature. Give up.
432+
if (prefix.empty() || prefixType->isTypeParameter()) {
433+
llvm::errs() << "\n";
434+
llvm::errs() << "getReducedTypeParameter() was called\n";
435+
llvm::errs() << " with " << Sig << ",\n";
436+
llvm::errs() << " and " << t << ".\n\n";
437+
if (prefix.empty()) {
438+
llvm::errs() << "This type parameter contains the generic parameter "
439+
<< Type(t->getRootGenericParam()) << ".\n\n";
440+
llvm::errs() << "This generic parameter is not part of the given "
441+
<< "generic signature.\n\n";
442+
} else {
443+
llvm::errs() << "This type parameter's reduced term is " << term << ".\n\n";
444+
llvm::errs() << "This is not a valid term, because " << prefix << " does not "
445+
<< "have a member type named " << term[prefix.size()] << ".\n\n";
446+
}
447+
llvm::errs() << "This usually indicates the caller passed the wrong type or "
448+
<< "generic signature to getReducedType().\n\n";
449+
450+
dump(llvm::errs());
451+
abort();
452+
}
453+
454+
// Compute the type of the unresolved suffix term V.
455+
auto substType = substPrefixType(t, term.size() - prefix.size(),
456+
prefixType, Sig);
457+
458+
// FIXME: Recursion guard is needed here
459+
return getReducedType(substType, genericParams);
460+
}
461+
348462
/// Unlike most other queries, the input type can be any type, not just a
349463
/// type parameter.
350464
///
@@ -375,117 +489,7 @@ Type RequirementMachine::getReducedType(
375489
if (!t->isTypeParameter())
376490
return std::nullopt;
377491

378-
// Get a simplified term T.
379-
auto term = Context.getMutableTermForType(t->getCanonicalType(),
380-
/*proto=*/nullptr);
381-
System.simplify(term);
382-
383-
// We need to handle "purely concrete" member types, eg if I have a
384-
// signature <T where T == Foo>, and we're asked to reduce the
385-
// type T.[P:A] where Foo : A.
386-
//
387-
// This comes up because we can derive the signature <T where T == Foo>
388-
// from a generic signature like <T where T : P>; adding the
389-
// concrete requirement 'T == Foo' renders 'T : P' redundant. We then
390-
// want to take interface types written against the original signature
391-
// and reduce them with respect to the derived signature.
392-
//
393-
// The problem is that T.[P:A] is not a valid term in the rewrite system
394-
// for <T where T == Foo>, since we do not have the requirement T : P.
395-
//
396-
// A more principled solution would build a substitution map when
397-
// building a derived generic signature that adds new requirements;
398-
// interface types would first be substituted before being reduced
399-
// in the new signature.
400-
//
401-
// For now, we handle this with a two-step process; we split a term up
402-
// into a longest valid prefix, which must resolve to a concrete type,
403-
// and the remaining suffix, which we use to perform a concrete
404-
// substitution using subst().
405-
406-
// In the below, let T be a type term, with T == UV, where U is the
407-
// longest valid prefix.
408-
//
409-
// Note that V can be empty if T is fully valid; we expect this to be
410-
// true most of the time.
411-
auto prefix = getLongestValidPrefix(term);
412-
413-
// Get a type (concrete or dependent) for U.
414-
auto prefixType = [&]() -> Type {
415-
if (prefix.empty())
416-
return Type();
417-
418-
verify(prefix);
419-
420-
auto *props = Map.lookUpProperties(prefix);
421-
if (props) {
422-
if (props->isConcreteType()) {
423-
auto concreteType = props->getConcreteType(genericParams,
424-
prefix, Map);
425-
if (!concreteType->hasTypeParameter())
426-
return concreteType;
427-
428-
// FIXME: Recursion guard is needed here
429-
return getReducedType(concreteType, genericParams);
430-
}
431-
432-
// Skip this part if the entire input term is valid, because in that
433-
// case we don't want to replace the term with its superclass bound;
434-
// unlike a fixed concrete type, the superclass bound only comes into
435-
// play when looking up a member type.
436-
if (props->hasSuperclassBound() &&
437-
prefix.size() != term.size()) {
438-
auto superclass = props->getSuperclassBound(genericParams,
439-
prefix, Map);
440-
if (!superclass->hasTypeParameter())
441-
return superclass;
442-
443-
// FIXME: Recursion guard is needed here
444-
return getReducedType(superclass, genericParams);
445-
}
446-
}
447-
448-
return Map.getTypeForTerm(prefix, genericParams);
449-
}();
450-
451-
// If T is already valid, the longest valid prefix U of T is T itself, and
452-
// V is empty. Just return the type we computed above.
453-
//
454-
// This is the only case where U is allowed to be dependent.
455-
if (prefix.size() == term.size())
456-
return prefixType;
457-
458-
// If U is not concrete, we have an invalid member type of a dependent
459-
// type, which is not valid in this generic signature. Give up.
460-
if (prefix.empty() || prefixType->isTypeParameter()) {
461-
llvm::errs() << "\n";
462-
llvm::errs() << "getReducedType() was called\n";
463-
llvm::errs() << " with " << Sig << ",\n";
464-
llvm::errs() << " and " << type << ".\n\n";
465-
llvm::errs() << "This type contains the type parameter " << t << ".\n\n";
466-
if (prefix.empty()) {
467-
llvm::errs() << "This type parameter contains the generic parameter "
468-
<< Type(t->getRootGenericParam()) << ".\n\n";
469-
llvm::errs() << "This generic parameter is not part of the given "
470-
<< "generic signature.\n\n";
471-
} else {
472-
llvm::errs() << "This type parameter's reduced term is " << term << ".\n\n";
473-
llvm::errs() << "This is not a valid term, because " << prefix << " does not "
474-
<< "have a member type named " << term[prefix.size()] << ".\n\n";
475-
}
476-
llvm::errs() << "This usually indicates the caller passed the wrong type or "
477-
<< "generic signature to getReducedType().\n\n";
478-
479-
dump(llvm::errs());
480-
abort();
481-
}
482-
483-
// Compute the type of the unresolved suffix term V.
484-
auto substType = substPrefixType(t, term.size() - prefix.size(),
485-
prefixType, Sig);
486-
487-
// FIXME: Recursion guard is needed here
488-
return getReducedType(substType, genericParams);
492+
return getReducedTypeParameter(t->getCanonicalType(), genericParams);
489493
});
490494
}
491495

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ class RequirementMachine final {
154154
const ProtocolDecl *proto=nullptr) const;
155155
bool areReducedTypeParametersEqual(Type depType1, Type depType2) const;
156156
bool isReducedType(Type type) const;
157+
Type getReducedTypeParameter(CanType type,
158+
ArrayRef<GenericTypeParamType *> genericParams) const;
157159
Type getReducedType(Type type,
158160
ArrayRef<GenericTypeParamType *> genericParams) const;
159161
bool isValidTypeParameter(Type type) const;

0 commit comments

Comments
 (0)