Skip to content

Commit 568d99f

Browse files
committed
Sema: Clean up @_specialize diagnostics
Instead of looking at the requirements before passing them off to the GSB, let's look at the difference between the final signature and the original signature. This makes the checks more accurate, for example we want to detect that 'E' was specialized in the below: @_specialize(where S == Set<String>) public func takesSequenceAndElement<S, E>(_: S, _: E) where S : Sequence, E == S.Element {}
1 parent 17e3ee7 commit 568d99f

File tree

3 files changed

+118
-174
lines changed

3 files changed

+118
-174
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5403,21 +5403,17 @@ ERROR(specialize_missing_where_clause,none,
54035403
ERROR(specialize_empty_where_clause,none,
54045404
"empty 'where' clause in '_specialize' attribute", ())
54055405
ERROR(specialize_attr_non_concrete_same_type_req,none,
5406-
"Only concrete type same-type requirements are supported by '_specialize' attribute", ())
5406+
"only concrete type same-type requirements are supported by '_specialize' attribute", ())
54075407
ERROR(specialize_attr_only_generic_param_req,none,
5408-
"Only requirements on generic parameters are supported by '_specialize' attribute", ())
5409-
ERROR(specialize_attr_only_one_concrete_same_type_req,none,
5410-
"Only one concrete type should be used in the same-type requirement in '_specialize' attribute", ())
5411-
ERROR(specialize_attr_non_protocol_type_constraint_req,none,
5412-
"Only conformances to protocol types are supported by '_specialize' attribute", ())
5408+
"only requirements on generic parameters are supported by '_specialize' attribute", ())
54135409
ERROR(specialize_attr_type_parameter_count_mismatch,none,
5414-
"%select{too many|too few}2 type parameters are specified "
5415-
"in '_specialize' attribute (got %1, but expected %0)",
5416-
(unsigned, unsigned, bool))
5417-
ERROR(specialize_attr_missing_constraint,none,
5418-
"Missing constraint for %0 in '_specialize' attribute", (DeclName))
5410+
"too few generic parameters are specified "
5411+
"in '_specialize' attribute (got %0, but expected %1)",
5412+
(unsigned, unsigned))
5413+
NOTE(specialize_attr_missing_constraint,none,
5414+
"missing constraint for %0 in '_specialize' attribute", (DeclName))
54195415
ERROR(specialize_attr_unsupported_kind_of_req,none,
5420-
"Only same-type and layout requirements are supported by '_specialize' attribute", ())
5416+
"only same-type and layout requirements are supported by '_specialize' attribute", ())
54215417
ERROR(specialize_target_function_not_found, none,
54225418
"target function %0 could not be found", (DeclNameRef))
54235419
ERROR(specialize_target_function_of_type_not_found, none,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 62 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,71 +2082,79 @@ void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) {
20822082
attr->setInvalid();
20832083
}
20842084

2085-
/// Collect all used generic parameter types from a given type.
2086-
static void collectUsedGenericParameters(
2087-
Type Ty, SmallPtrSetImpl<TypeBase *> &ConstrainedGenericParams) {
2088-
if (!Ty)
2089-
return;
2085+
/// Ensure that the requirements provided by the @_specialize attribute
2086+
/// can be supported by the SIL EagerSpecializer pass.
2087+
static void checkSpecializeAttrRequirements(SpecializeAttr *attr,
2088+
GenericSignature originalSig,
2089+
GenericSignature specializedSig,
2090+
ASTContext &ctx) {
2091+
bool hadError = false;
2092+
2093+
auto specializedReqs = specializedSig->requirementsNotSatisfiedBy(originalSig);
2094+
for (auto specializedReq : specializedReqs) {
2095+
if (!specializedReq.getFirstType()->is<GenericTypeParamType>()) {
2096+
ctx.Diags.diagnose(attr->getLocation(),
2097+
diag::specialize_attr_only_generic_param_req);
2098+
hadError = true;
2099+
continue;
2100+
}
20902101

2091-
if (!Ty->hasTypeParameter())
2092-
return;
2102+
switch (specializedReq.getKind()) {
2103+
case RequirementKind::Conformance:
2104+
case RequirementKind::Superclass:
2105+
ctx.Diags.diagnose(attr->getLocation(),
2106+
diag::specialize_attr_unsupported_kind_of_req);
2107+
hadError = true;
2108+
break;
2109+
2110+
case RequirementKind::SameType:
2111+
if (specializedReq.getSecondType()->isTypeParameter()) {
2112+
ctx.Diags.diagnose(attr->getLocation(),
2113+
diag::specialize_attr_non_concrete_same_type_req);
2114+
hadError = true;
2115+
}
2116+
break;
20932117

2094-
// Add used generic parameters/archetypes.
2095-
Ty.visit([&](Type Ty) {
2096-
if (auto GP = dyn_cast<GenericTypeParamType>(Ty->getCanonicalType())) {
2097-
ConstrainedGenericParams.insert(GP);
2118+
case RequirementKind::Layout:
2119+
break;
20982120
}
2099-
});
2100-
}
2121+
}
21012122

2102-
/// Perform some sanity checks for the requirements provided by
2103-
/// the @_specialize attribute.
2104-
static void checkSpecializeAttrRequirements(
2105-
SpecializeAttr *attr,
2106-
AbstractFunctionDecl *FD,
2107-
const SmallPtrSet<TypeBase *, 4> &constrainedGenericParams,
2108-
ASTContext &ctx) {
2109-
auto genericSig = FD->getGenericSignature();
2123+
if (hadError)
2124+
return;
21102125

21112126
if (!attr->isFullSpecialization())
21122127
return;
21132128

2114-
if (constrainedGenericParams.size() == genericSig->getGenericParams().size())
2129+
if (specializedSig->areAllParamsConcrete())
21152130
return;
21162131

2117-
ctx.Diags.diagnose(
2118-
attr->getLocation(), diag::specialize_attr_type_parameter_count_mismatch,
2119-
genericSig->getGenericParams().size(), constrainedGenericParams.size(),
2120-
constrainedGenericParams.size() < genericSig->getGenericParams().size());
2132+
SmallVector<GenericTypeParamType *, 2> unspecializedParams;
21212133

2122-
if (constrainedGenericParams.size() < genericSig->getGenericParams().size()) {
2123-
// Figure out which archetypes are not constrained.
2124-
for (auto gp : genericSig->getGenericParams()) {
2125-
if (constrainedGenericParams.count(gp->getCanonicalType().getPointer()))
2126-
continue;
2127-
auto gpDecl = gp->getDecl();
2128-
if (gpDecl) {
2129-
ctx.Diags.diagnose(attr->getLocation(),
2130-
diag::specialize_attr_missing_constraint,
2131-
gpDecl->getName());
2132-
}
2134+
for (auto *paramTy : specializedSig->getGenericParams()) {
2135+
auto canTy = paramTy->getCanonicalType();
2136+
if (specializedSig->isCanonicalTypeInContext(canTy) &&
2137+
(!specializedSig->getLayoutConstraint(canTy) ||
2138+
originalSig->getLayoutConstraint(canTy))) {
2139+
unspecializedParams.push_back(paramTy);
21332140
}
21342141
}
2135-
}
21362142

2137-
/// Require that the given type either not involve type parameters or be
2138-
/// a type parameter.
2139-
static bool diagnoseIndirectGenericTypeParam(SourceLoc loc, Type type,
2140-
TypeRepr *typeRepr) {
2141-
if (type->hasTypeParameter() && !type->is<GenericTypeParamType>()) {
2142-
type->getASTContext().Diags.diagnose(
2143-
loc,
2144-
diag::specialize_attr_only_generic_param_req)
2145-
.highlight(typeRepr->getSourceRange());
2146-
return true;
2147-
}
2143+
unsigned expectedCount = specializedSig->getGenericParams().size();
2144+
unsigned gotCount = expectedCount - unspecializedParams.size();
21482145

2149-
return false;
2146+
if (expectedCount == gotCount)
2147+
return;
2148+
2149+
ctx.Diags.diagnose(
2150+
attr->getLocation(), diag::specialize_attr_type_parameter_count_mismatch,
2151+
gotCount, expectedCount);
2152+
2153+
for (auto paramTy : unspecializedParams) {
2154+
ctx.Diags.diagnose(attr->getLocation(),
2155+
diag::specialize_attr_missing_constraint,
2156+
paramTy->getName());
2157+
}
21502158
}
21512159

21522160
/// Type check that a set of requirements provided by @_specialize.
@@ -2183,93 +2191,9 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
21832191
// First, add the old generic signature.
21842192
Builder.addGenericSignature(genericSig);
21852193

2186-
// Set of generic parameters being constrained. It is used to
2187-
// determine if a full specialization misses requirements for
2188-
// some of the generic parameters.
2189-
SmallPtrSet<TypeBase *, 4> constrainedGenericParams;
2190-
21912194
// Go over the set of requirements, adding them to the builder.
21922195
WhereClauseOwner(FD, attr).visitRequirements(TypeResolutionStage::Interface,
21932196
[&](const Requirement &req, RequirementRepr *reqRepr) {
2194-
// Collect all of the generic parameters used by these types.
2195-
switch (req.getKind()) {
2196-
case RequirementKind::Conformance:
2197-
case RequirementKind::SameType:
2198-
case RequirementKind::Superclass:
2199-
collectUsedGenericParameters(req.getSecondType(),
2200-
constrainedGenericParams);
2201-
LLVM_FALLTHROUGH;
2202-
2203-
case RequirementKind::Layout:
2204-
collectUsedGenericParameters(req.getFirstType(),
2205-
constrainedGenericParams);
2206-
break;
2207-
}
2208-
2209-
// Check additional constraints.
2210-
// FIXME: These likely aren't fundamental limitations.
2211-
switch (req.getKind()) {
2212-
case RequirementKind::SameType: {
2213-
bool firstHasTypeParameter = req.getFirstType()->hasTypeParameter();
2214-
bool secondHasTypeParameter = req.getSecondType()->hasTypeParameter();
2215-
2216-
// Exactly one type can have a type parameter.
2217-
if (firstHasTypeParameter == secondHasTypeParameter) {
2218-
diagnose(attr->getLocation(),
2219-
firstHasTypeParameter
2220-
? diag::specialize_attr_non_concrete_same_type_req
2221-
: diag::specialize_attr_only_one_concrete_same_type_req)
2222-
.highlight(reqRepr->getSourceRange());
2223-
return false;
2224-
}
2225-
2226-
// We either need a fully-concrete type or a generic type parameter.
2227-
if (diagnoseIndirectGenericTypeParam(attr->getLocation(),
2228-
req.getFirstType(),
2229-
reqRepr->getFirstTypeRepr()) ||
2230-
diagnoseIndirectGenericTypeParam(attr->getLocation(),
2231-
req.getSecondType(),
2232-
reqRepr->getSecondTypeRepr())) {
2233-
return false;
2234-
}
2235-
break;
2236-
}
2237-
2238-
case RequirementKind::Superclass:
2239-
diagnose(attr->getLocation(),
2240-
diag::specialize_attr_non_protocol_type_constraint_req)
2241-
.highlight(reqRepr->getSourceRange());
2242-
return false;
2243-
2244-
case RequirementKind::Conformance:
2245-
if (diagnoseIndirectGenericTypeParam(attr->getLocation(),
2246-
req.getFirstType(),
2247-
reqRepr->getSubjectRepr())) {
2248-
return false;
2249-
}
2250-
2251-
if (!req.getSecondType()->is<ProtocolType>()) {
2252-
diagnose(attr->getLocation(),
2253-
diag::specialize_attr_non_protocol_type_constraint_req)
2254-
.highlight(reqRepr->getSourceRange());
2255-
return false;
2256-
}
2257-
2258-
diagnose(attr->getLocation(),
2259-
diag::specialize_attr_unsupported_kind_of_req)
2260-
.highlight(reqRepr->getSourceRange());
2261-
2262-
return false;
2263-
2264-
case RequirementKind::Layout:
2265-
if (diagnoseIndirectGenericTypeParam(attr->getLocation(),
2266-
req.getFirstType(),
2267-
reqRepr->getSubjectRepr())) {
2268-
return false;
2269-
}
2270-
break;
2271-
}
2272-
22732197
// Add the requirement to the generic signature builder.
22742198
using FloatingRequirementSource =
22752199
GenericSignatureBuilder::FloatingRequirementSource;
@@ -2279,12 +2203,13 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
22792203
return false;
22802204
});
22812205

2282-
// Check the validity of provided requirements.
2283-
checkSpecializeAttrRequirements(attr, FD, constrainedGenericParams, Ctx);
2284-
22852206
// Check the result.
22862207
auto specializedSig = std::move(Builder).computeGenericSignature(
22872208
/*allowConcreteGenericParams=*/true);
2209+
2210+
// Check the validity of provided requirements.
2211+
checkSpecializeAttrRequirements(attr, genericSig, specializedSig, Ctx);
2212+
22882213
attr->setSpecializedSignature(specializedSig);
22892214

22902215
// Check the target function if there is one.

0 commit comments

Comments
 (0)