Skip to content

Commit 91ebe44

Browse files
committed
GSB: Move signature verification from CanGenericSignature::getCanonical() to computeGenericSignature()
Doing this when computing a canonical signature didn't really make sense because canonical signatures are not canonicalized any more strongly _with respect to the builder_; they just canonicalize their requirement types. Instead, let's do these checks after creating the signature in computeGenericSignature(). The old behavior had another undesirable property; since the canonicalization was done by registerGenericSignatureBuilder(), we would always build a new GSB from scratch for every signature we compute. The new location also means we do these checks for protocol requirement signatures as well. This flags an existing fixed crasher where we still emit bogus same-type requirements in the requirement signature, so I moved this test back into an unfixed state.
1 parent 566abca commit 91ebe44

File tree

6 files changed

+197
-178
lines changed

6 files changed

+197
-178
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class CanGenericSignature : public GenericSignature {
175175
/// requirements, first canonicalizing the types.
176176
static CanGenericSignature
177177
getCanonical(TypeArrayView<GenericTypeParamType> params,
178-
ArrayRef<Requirement> requirements, bool skipValidation = false);
178+
ArrayRef<Requirement> requirements);
179179

180180
public:
181181
CanGenericSignature(std::nullptr_t) : GenericSignature(nullptr) {}
@@ -342,6 +342,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
342342
///
343343
/// The type parameters must be known to not be concrete within the context.
344344
bool areSameTypeParameterInContext(Type type1, Type type2) const;
345+
bool areSameTypeParameterInContext(Type type1, Type type2,
346+
GenericSignatureBuilder &builder) const;
345347

346348
/// Determine if \c sig can prove \c requirement, meaning that it can deduce
347349
/// T: Foo or T == U (etc.) with the information it knows. This includes
@@ -360,12 +362,10 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
360362
/// Return the canonical version of the given type under this generic
361363
/// signature.
362364
CanType getCanonicalTypeInContext(Type type) const;
363-
bool isCanonicalTypeInContext(Type type) const;
364-
365-
/// Return the canonical version of the given type under this generic
366-
/// signature.
367365
CanType getCanonicalTypeInContext(Type type,
368366
GenericSignatureBuilder &builder) const;
367+
368+
bool isCanonicalTypeInContext(Type type) const;
369369
bool isCanonicalTypeInContext(Type type,
370370
GenericSignatureBuilder &builder) const;
371371

lib/AST/GenericSignature.cpp

Lines changed: 14 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,9 @@ bool GenericSignatureImpl::isCanonical() const {
176176
return getCanonicalSignature().getPointer() == this;
177177
}
178178

179-
#ifndef NDEBUG
180-
/// Determine the canonical ordering of requirements.
181-
static unsigned getRequirementKindOrder(RequirementKind kind) {
182-
switch (kind) {
183-
case RequirementKind::Conformance: return 2;
184-
case RequirementKind::Superclass: return 0;
185-
case RequirementKind::SameType: return 3;
186-
case RequirementKind::Layout: return 1;
187-
}
188-
llvm_unreachable("unhandled kind");
189-
}
190-
#endif
191-
192179
CanGenericSignature
193180
CanGenericSignature::getCanonical(TypeArrayView<GenericTypeParamType> params,
194-
ArrayRef<Requirement> requirements,
195-
bool skipValidation) {
181+
ArrayRef<Requirement> requirements) {
196182
// Canonicalize the parameters and requirements.
197183
SmallVector<GenericTypeParamType*, 8> canonicalParams;
198184
canonicalParams.reserve(params.size());
@@ -205,116 +191,9 @@ CanGenericSignature::getCanonical(TypeArrayView<GenericTypeParamType> params,
205191
for (auto &reqt : requirements)
206192
canonicalRequirements.push_back(reqt.getCanonical());
207193

208-
(void)skipValidation;
209194
auto canSig = get(canonicalParams, canonicalRequirements,
210195
/*isKnownCanonical=*/true);
211196

212-
#ifndef NDEBUG
213-
if (skipValidation)
214-
return CanGenericSignature(canSig);
215-
216-
PrettyStackTraceGenericSignature debugStack("canonicalizing", canSig);
217-
218-
// Check that the signature is canonical.
219-
for (unsigned idx : indices(canonicalRequirements)) {
220-
debugStack.setRequirement(idx);
221-
222-
const auto &reqt = canonicalRequirements[idx];
223-
224-
// Left-hand side must be canonical in its context.
225-
// Check canonicalization of requirement itself.
226-
switch (reqt.getKind()) {
227-
case RequirementKind::Superclass:
228-
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType()) &&
229-
"Left-hand side is not canonical");
230-
assert(canSig->isCanonicalTypeInContext(reqt.getSecondType()) &&
231-
"Superclass type isn't canonical in its own context");
232-
break;
233-
234-
case RequirementKind::Layout:
235-
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType()) &&
236-
"Left-hand side is not canonical");
237-
break;
238-
239-
case RequirementKind::SameType: {
240-
auto isCanonicalAnchor = [&](Type type) {
241-
if (auto *dmt = type->getAs<DependentMemberType>())
242-
return canSig->isCanonicalTypeInContext(dmt->getBase());
243-
return type->is<GenericTypeParamType>();
244-
};
245-
246-
auto firstType = reqt.getFirstType();
247-
auto secondType = reqt.getSecondType();
248-
assert(isCanonicalAnchor(firstType));
249-
250-
if (reqt.getSecondType()->isTypeParameter()) {
251-
assert(isCanonicalAnchor(secondType));
252-
assert(compareDependentTypes(firstType, secondType) < 0 &&
253-
"Out-of-order type parameters in same-type constraint");
254-
} else {
255-
assert(canSig->isCanonicalTypeInContext(secondType) &&
256-
"Concrete same-type isn't canonical in its own context");
257-
}
258-
break;
259-
}
260-
261-
case RequirementKind::Conformance:
262-
assert(reqt.getFirstType()->isTypeParameter() &&
263-
"Left-hand side must be a type parameter");
264-
assert(isa<ProtocolType>(reqt.getSecondType().getPointer()) &&
265-
"Right-hand side of conformance isn't a protocol type");
266-
break;
267-
}
268-
269-
// From here on, we're only interested in requirements beyond the first.
270-
if (idx == 0) continue;
271-
272-
// Make sure that the left-hand sides are in nondecreasing order.
273-
const auto &prevReqt = canonicalRequirements[idx-1];
274-
int compareLHS =
275-
compareDependentTypes(prevReqt.getFirstType(), reqt.getFirstType());
276-
assert(compareLHS <= 0 && "Out-of-order left-hand sides");
277-
278-
// If we have two same-type requirements where the left-hand sides differ
279-
// but fall into the same equivalence class, we can check the form.
280-
if (compareLHS < 0 && reqt.getKind() == RequirementKind::SameType &&
281-
prevReqt.getKind() == RequirementKind::SameType &&
282-
canSig->areSameTypeParameterInContext(prevReqt.getFirstType(),
283-
reqt.getFirstType())) {
284-
// If it's a it's a type parameter, make sure the equivalence class is
285-
// wired together sanely.
286-
if (prevReqt.getSecondType()->isTypeParameter()) {
287-
assert(prevReqt.getSecondType()->isEqual(reqt.getFirstType()) &&
288-
"same-type constraints within an equiv. class are out-of-order");
289-
} else {
290-
// Otherwise, the concrete types must match up.
291-
assert(prevReqt.getSecondType()->isEqual(reqt.getSecondType()) &&
292-
"inconsistent concrete same-type constraints in equiv. class");
293-
}
294-
}
295-
296-
// From here on, we only care about cases where the previous and current
297-
// requirements have the same left-hand side.
298-
if (compareLHS != 0) continue;
299-
300-
// Check ordering of requirement kinds.
301-
assert((getRequirementKindOrder(prevReqt.getKind()) <=
302-
getRequirementKindOrder(reqt.getKind())) &&
303-
"Requirements for a given kind are out-of-order");
304-
305-
// From here on, we only care about the same requirement kind.
306-
if (prevReqt.getKind() != reqt.getKind()) continue;
307-
308-
assert(reqt.getKind() == RequirementKind::Conformance &&
309-
"Only conformance requirements can have multiples");
310-
311-
auto prevProto = prevReqt.getProtocolDecl();
312-
auto proto = reqt.getProtocolDecl();
313-
assert(TypeDecl::compare(prevProto, proto) < 0 &&
314-
"Out-of-order conformance requirements");
315-
}
316-
#endif
317-
318197
return CanGenericSignature(canSig);
319198
}
320199

@@ -519,7 +398,19 @@ bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1,
519398
if (type1.getPointer() == type2.getPointer())
520399
return true;
521400

522-
auto &builder = *getGenericSignatureBuilder();
401+
return areSameTypeParameterInContext(type1, type2,
402+
*getGenericSignatureBuilder());
403+
}
404+
405+
bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1,
406+
Type type2,
407+
GenericSignatureBuilder &builder) const {
408+
assert(type1->isTypeParameter());
409+
assert(type2->isTypeParameter());
410+
411+
if (type1.getPointer() == type2.getPointer())
412+
return true;
413+
523414
auto equivClass1 =
524415
builder.resolveEquivalenceClass(
525416
type1,

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7948,6 +7948,129 @@ void GenericSignatureBuilder::addGenericSignature(GenericSignature sig) {
79487948
addRequirement(reqt, FloatingRequirementSource::forAbstract(), nullptr);
79497949
}
79507950

7951+
#ifndef NDEBUG
7952+
7953+
/// Determine the canonical ordering of requirements.
7954+
static unsigned getRequirementKindOrder(RequirementKind kind) {
7955+
switch (kind) {
7956+
case RequirementKind::Conformance: return 2;
7957+
case RequirementKind::Superclass: return 0;
7958+
case RequirementKind::SameType: return 3;
7959+
case RequirementKind::Layout: return 1;
7960+
}
7961+
llvm_unreachable("unhandled kind");
7962+
}
7963+
7964+
static void checkGenericSignature(CanGenericSignature canSig,
7965+
GenericSignatureBuilder &builder) {
7966+
PrettyStackTraceGenericSignature debugStack("checking", canSig);
7967+
7968+
auto canonicalRequirements = canSig->getRequirements();
7969+
7970+
// Check that the signature is canonical.
7971+
for (unsigned idx : indices(canonicalRequirements)) {
7972+
debugStack.setRequirement(idx);
7973+
7974+
const auto &reqt = canonicalRequirements[idx];
7975+
7976+
// Left-hand side must be canonical in its context.
7977+
// Check canonicalization of requirement itself.
7978+
switch (reqt.getKind()) {
7979+
case RequirementKind::Superclass:
7980+
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
7981+
"Left-hand side is not canonical");
7982+
assert(canSig->isCanonicalTypeInContext(reqt.getSecondType(), builder) &&
7983+
"Superclass type isn't canonical in its own context");
7984+
break;
7985+
7986+
case RequirementKind::Layout:
7987+
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
7988+
"Left-hand side is not canonical");
7989+
break;
7990+
7991+
case RequirementKind::SameType: {
7992+
auto isCanonicalAnchor = [&](Type type) {
7993+
if (auto *dmt = type->getAs<DependentMemberType>())
7994+
return canSig->isCanonicalTypeInContext(dmt->getBase(), builder);
7995+
return type->is<GenericTypeParamType>();
7996+
};
7997+
7998+
auto firstType = reqt.getFirstType();
7999+
auto secondType = reqt.getSecondType();
8000+
assert(isCanonicalAnchor(firstType));
8001+
8002+
if (reqt.getSecondType()->isTypeParameter()) {
8003+
assert(isCanonicalAnchor(secondType));
8004+
assert(compareDependentTypes(firstType, secondType) < 0 &&
8005+
"Out-of-order type parameters in same-type constraint");
8006+
} else {
8007+
assert(canSig->isCanonicalTypeInContext(secondType) &&
8008+
"Concrete same-type isn't canonical in its own context");
8009+
}
8010+
break;
8011+
}
8012+
8013+
case RequirementKind::Conformance:
8014+
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
8015+
"Left-hand side is not canonical");
8016+
assert(reqt.getFirstType()->isTypeParameter() &&
8017+
"Left-hand side must be a type parameter");
8018+
assert(isa<ProtocolType>(reqt.getSecondType().getPointer()) &&
8019+
"Right-hand side of conformance isn't a protocol type");
8020+
break;
8021+
}
8022+
8023+
// From here on, we're only interested in requirements beyond the first.
8024+
if (idx == 0) continue;
8025+
8026+
// Make sure that the left-hand sides are in nondecreasing order.
8027+
const auto &prevReqt = canonicalRequirements[idx-1];
8028+
int compareLHS =
8029+
compareDependentTypes(prevReqt.getFirstType(), reqt.getFirstType());
8030+
assert(compareLHS <= 0 && "Out-of-order left-hand sides");
8031+
8032+
// If we have two same-type requirements where the left-hand sides differ
8033+
// but fall into the same equivalence class, we can check the form.
8034+
if (compareLHS < 0 && reqt.getKind() == RequirementKind::SameType &&
8035+
prevReqt.getKind() == RequirementKind::SameType &&
8036+
canSig->areSameTypeParameterInContext(prevReqt.getFirstType(),
8037+
reqt.getFirstType(),
8038+
builder)) {
8039+
// If it's a it's a type parameter, make sure the equivalence class is
8040+
// wired together sanely.
8041+
if (prevReqt.getSecondType()->isTypeParameter()) {
8042+
assert(prevReqt.getSecondType()->isEqual(reqt.getFirstType()) &&
8043+
"same-type constraints within an equiv. class are out-of-order");
8044+
} else {
8045+
// Otherwise, the concrete types must match up.
8046+
assert(prevReqt.getSecondType()->isEqual(reqt.getSecondType()) &&
8047+
"inconsistent concrete same-type constraints in equiv. class");
8048+
}
8049+
}
8050+
8051+
// From here on, we only care about cases where the previous and current
8052+
// requirements have the same left-hand side.
8053+
if (compareLHS != 0) continue;
8054+
8055+
// Check ordering of requirement kinds.
8056+
assert((getRequirementKindOrder(prevReqt.getKind()) <=
8057+
getRequirementKindOrder(reqt.getKind())) &&
8058+
"Requirements for a given kind are out-of-order");
8059+
8060+
// From here on, we only care about the same requirement kind.
8061+
if (prevReqt.getKind() != reqt.getKind()) continue;
8062+
8063+
assert(reqt.getKind() == RequirementKind::Conformance &&
8064+
"Only conformance requirements can have multiples");
8065+
8066+
auto prevProto = prevReqt.getProtocolDecl();
8067+
auto proto = reqt.getProtocolDecl();
8068+
assert(TypeDecl::compare(prevProto, proto) < 0 &&
8069+
"Out-of-order conformance requirements");
8070+
}
8071+
}
8072+
#endif
8073+
79518074
GenericSignature GenericSignatureBuilder::computeGenericSignature(
79528075
bool allowConcreteGenericParams,
79538076
bool allowBuilderToMove) && {
@@ -7961,6 +8084,12 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
79618084
// Form the generic signature.
79628085
auto sig = GenericSignature::get(getGenericParams(), requirements);
79638086

8087+
#ifndef NDEBUG
8088+
if (!Impl->HadAnyError) {
8089+
checkGenericSignature(sig.getCanonicalSignature(), *this);
8090+
}
8091+
#endif
8092+
79648093
// When we can, move this generic signature builder to make it the canonical
79658094
// builder, rather than constructing a new generic signature builder that
79668095
// will produce the same thing.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,13 +2452,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24522452
requirementsSig->print(llvm::errs());
24532453
llvm::errs() << "\n";
24542454

2455-
// Note: One cannot canonicalize a requirement signature, because
2456-
// requirement signatures are necessarily missing requirements.
24572455
llvm::errs() << "Canonical requirement signature: ";
24582456
auto canRequirementSig =
24592457
CanGenericSignature::getCanonical(requirementsSig->getGenericParams(),
2460-
requirementsSig->getRequirements(),
2461-
/*skipValidation=*/true);
2458+
requirementsSig->getRequirements());
24622459
canRequirementSig->print(llvm::errs());
24632460
llvm::errs() << "\n";
24642461
}

0 commit comments

Comments
 (0)