Skip to content

Commit 7d05a58

Browse files
committed
[ConstraintSystem] Fix a couple of issues related to generic specialization
- Teach specialization constraint to look through non-generic typealiases - Detect and diagnose attempts to specialize non-generic types/aliases - Detect and diagnose attempts to specialize types with invalid number of arguments (cherry picked from commit 42f80fb) (cherry picked from commit cbaf558) (cherry picked from commit ab2f47b) (cherry picked from commit 40169c7) (cherry picked from commit 2bbda09) (cherry picked from commit cd057bb) (cherry picked from commit be4df5a) (cherry picked from commit b9b21fc) (cherry picked from commit de729e2)
1 parent fc0ddf2 commit 7d05a58

File tree

7 files changed

+261
-9
lines changed

7 files changed

+261
-9
lines changed

include/swift/Sema/CSFix.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ enum class FixKind : uint8_t {
454454
/// Ignore the fact that member couldn't be referenced within init accessor
455455
/// because its name doesn't appear in 'initializes' or 'accesses' attributes.
456456
AllowInvalidMemberReferenceInInitAccessor,
457+
458+
/// Ignore an attempt to specialize non-generic type.
459+
AllowConcreteTypeSpecialization,
460+
461+
/// Ignore situations when provided number of generic arguments didn't match
462+
/// expected number of parameters.
463+
IgnoreGenericSpecializationArityMismatch,
457464
};
458465

459466
class ConstraintFix {
@@ -3647,6 +3654,69 @@ class AllowInvalidMemberReferenceInInitAccessor final : public ConstraintFix {
36473654
}
36483655
};
36493656

3657+
class AllowConcreteTypeSpecialization final : public ConstraintFix {
3658+
Type ConcreteType;
3659+
3660+
AllowConcreteTypeSpecialization(ConstraintSystem &cs, Type concreteTy,
3661+
ConstraintLocator *locator)
3662+
: ConstraintFix(cs, FixKind::AllowConcreteTypeSpecialization, locator),
3663+
ConcreteType(concreteTy) {}
3664+
3665+
public:
3666+
std::string getName() const override {
3667+
return "allow concrete type specialization";
3668+
}
3669+
3670+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3671+
3672+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3673+
return diagnose(*commonFixes.front().first);
3674+
}
3675+
3676+
static AllowConcreteTypeSpecialization *
3677+
create(ConstraintSystem &cs, Type concreteTy, ConstraintLocator *locator);
3678+
3679+
static bool classof(const ConstraintFix *fix) {
3680+
return fix->getKind() == FixKind::AllowConcreteTypeSpecialization;
3681+
}
3682+
};
3683+
3684+
class IgnoreGenericSpecializationArityMismatch final : public ConstraintFix {
3685+
ValueDecl *D;
3686+
unsigned NumParams;
3687+
unsigned NumArgs;
3688+
bool HasParameterPack;
3689+
3690+
IgnoreGenericSpecializationArityMismatch(ConstraintSystem &cs,
3691+
ValueDecl *decl, unsigned numParams,
3692+
unsigned numArgs,
3693+
bool hasParameterPack,
3694+
ConstraintLocator *locator)
3695+
: ConstraintFix(cs, FixKind::IgnoreGenericSpecializationArityMismatch,
3696+
locator),
3697+
D(decl), NumParams(numParams), NumArgs(numArgs),
3698+
HasParameterPack(hasParameterPack) {}
3699+
3700+
public:
3701+
std::string getName() const override {
3702+
return "ignore generic specialization mismatch";
3703+
}
3704+
3705+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3706+
3707+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3708+
return diagnose(*commonFixes.front().first);
3709+
}
3710+
3711+
static IgnoreGenericSpecializationArityMismatch *
3712+
create(ConstraintSystem &cs, ValueDecl *decl, unsigned numParams,
3713+
unsigned numArgs, bool hasParameterPack, ConstraintLocator *locator);
3714+
3715+
static bool classof(const ConstraintFix *fix) {
3716+
return fix->getKind() == FixKind::IgnoreGenericSpecializationArityMismatch;
3717+
}
3718+
};
3719+
36503720
} // end namespace constraints
36513721
} // end namespace swift
36523722

lib/Sema/CSDiagnostics.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9101,3 +9101,14 @@ bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() {
91019101
emitDiagnostic(diag::init_accessor_invalid_member_ref, MemberName);
91029102
return true;
91039103
}
9104+
9105+
bool ConcreteTypeSpecialization::diagnoseAsError() {
9106+
emitDiagnostic(diag::not_a_generic_type, ConcreteType);
9107+
return true;
9108+
}
9109+
9110+
bool InvalidTypeSpecializationArity::diagnoseAsError() {
9111+
emitDiagnostic(diag::type_parameter_count_mismatch, D->getBaseIdentifier(),
9112+
NumParams, NumArgs, NumArgs < NumParams, HasParameterPack);
9113+
return true;
9114+
}

lib/Sema/CSDiagnostics.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3036,6 +3036,50 @@ class InvalidMemberReferenceWithinInitAccessor final
30363036
bool diagnoseAsError() override;
30373037
};
30383038

3039+
/// Diagnose attempts to specialize a concrete type or its alias:
3040+
///
3041+
/// \code
3042+
/// struct Test {}
3043+
/// typealias X = Test
3044+
///
3045+
/// _ = X<Int>() // error
3046+
/// \endcode
3047+
class ConcreteTypeSpecialization final : public FailureDiagnostic {
3048+
Type ConcreteType;
3049+
3050+
public:
3051+
ConcreteTypeSpecialization(const Solution &solution, Type concreteTy,
3052+
ConstraintLocator *locator)
3053+
: FailureDiagnostic(solution, locator),
3054+
ConcreteType(resolveType(concreteTy)) {}
3055+
3056+
bool diagnoseAsError() override;
3057+
};
3058+
3059+
/// Diagnose attempts to specialize with invalid number of generic arguments:
3060+
///
3061+
/// \code
3062+
/// struct Test<T, U> {}
3063+
///
3064+
/// _ = Test<Int>() // error
3065+
/// \endcode
3066+
class InvalidTypeSpecializationArity final : public FailureDiagnostic {
3067+
ValueDecl *D;
3068+
unsigned NumParams;
3069+
unsigned NumArgs;
3070+
bool HasParameterPack;
3071+
3072+
public:
3073+
InvalidTypeSpecializationArity(const Solution &solution, ValueDecl *decl,
3074+
unsigned numParams, unsigned numArgs,
3075+
bool hasParameterPack,
3076+
ConstraintLocator *locator)
3077+
: FailureDiagnostic(solution, locator), D(decl), NumParams(numParams),
3078+
NumArgs(numArgs), HasParameterPack(hasParameterPack) {}
3079+
3080+
bool diagnoseAsError() override;
3081+
};
3082+
30393083
} // end namespace constraints
30403084
} // end namespace swift
30413085

lib/Sema/CSFix.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2837,3 +2837,34 @@ AllowInvalidMemberReferenceInInitAccessor::create(ConstraintSystem &cs,
28372837
return new (cs.getAllocator())
28382838
AllowInvalidMemberReferenceInInitAccessor(cs, memberName, locator);
28392839
}
2840+
2841+
bool AllowConcreteTypeSpecialization::diagnose(const Solution &solution,
2842+
bool asNote) const {
2843+
ConcreteTypeSpecialization failure(solution, ConcreteType, getLocator());
2844+
return failure.diagnose(asNote);
2845+
}
2846+
2847+
AllowConcreteTypeSpecialization *
2848+
AllowConcreteTypeSpecialization::create(ConstraintSystem &cs, Type concreteTy,
2849+
ConstraintLocator *locator) {
2850+
return new (cs.getAllocator())
2851+
AllowConcreteTypeSpecialization(cs, concreteTy, locator);
2852+
}
2853+
2854+
bool IgnoreGenericSpecializationArityMismatch::diagnose(
2855+
const Solution &solution, bool asNote) const {
2856+
InvalidTypeSpecializationArity failure(solution, D, NumParams, NumArgs,
2857+
HasParameterPack, getLocator());
2858+
return failure.diagnose(asNote);
2859+
}
2860+
2861+
IgnoreGenericSpecializationArityMismatch *
2862+
IgnoreGenericSpecializationArityMismatch::create(ConstraintSystem &cs,
2863+
ValueDecl *decl,
2864+
unsigned numParams,
2865+
unsigned numArgs,
2866+
bool hasParameterPack,
2867+
ConstraintLocator *locator) {
2868+
return new (cs.getAllocator()) IgnoreGenericSpecializationArityMismatch(
2869+
cs, decl, numParams, numArgs, hasParameterPack, locator);
2870+
}

lib/Sema/CSSimplify.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13598,21 +13598,41 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
1359813598
}
1359913599

1360013600
decl = overloadChoice.getDecl();
13601+
1360113602
auto openedOverloadTypes = getOpenedTypes(overloadLocator);
1360213603
openedTypes.append(openedOverloadTypes.begin(), openedOverloadTypes.end());
1360313604
}
1360413605

13605-
auto genericContext = decl->getAsGenericContext();
13606-
if (!genericContext)
13606+
std::function<GenericParamList *(ValueDecl *)> getGenericParams =
13607+
[&](ValueDecl *decl) -> GenericParamList * {
13608+
auto genericContext = decl->getAsGenericContext();
13609+
if (!genericContext)
13610+
return nullptr;
13611+
13612+
auto genericParams = genericContext->getGenericParams();
13613+
if (!genericParams) {
13614+
// If declaration is a non-generic typealias, let's point
13615+
// to the underlying generic declaration.
13616+
if (auto *TA = dyn_cast<TypeAliasDecl>(decl)) {
13617+
if (auto *UGT = TA->getUnderlyingType()->getAs<AnyGenericType>())
13618+
return getGenericParams(UGT->getDecl());
13619+
}
13620+
}
13621+
13622+
return genericParams;
13623+
};
13624+
13625+
if (!decl->getAsGenericContext())
1360713626
return SolutionKind::Error;
1360813627

13609-
auto genericParams = genericContext->getGenericParams();
13610-
if (!genericParams || genericParams->size() == 0) {
13628+
auto genericParams = getGenericParams(decl);
13629+
if (!genericParams) {
1361113630
// FIXME: Record an error here that we're ignoring the parameters.
1361213631
return SolutionKind::Solved;
1361313632
}
1361413633

1361513634
// Map the generic parameters we have over to their opened types.
13635+
bool hasParameterPack = false;
1361613636
SmallVector<Type, 2> openedGenericParams;
1361713637
auto genericParamDepth = genericParams->getParams()[0]->getDepth();
1361813638
for (const auto &openedType : openedTypes) {
@@ -13634,19 +13654,38 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
1363413654

1363513655
auto *expansion = PackExpansionType::get(patternType, shapeType);
1363613656
openedGenericParams.push_back(expansion);
13657+
hasParameterPack = true;
1363713658
} else {
1363813659
openedGenericParams.push_back(Type(openedType.second));
1363913660
}
1364013661
}
1364113662
}
13663+
13664+
if (openedGenericParams.empty()) {
13665+
if (!shouldAttemptFixes())
13666+
return SolutionKind::Error;
13667+
13668+
return recordFix(AllowConcreteTypeSpecialization::create(
13669+
*this, type1, getConstraintLocator(locator)))
13670+
? SolutionKind::Error
13671+
: SolutionKind::Solved;
13672+
}
13673+
1364213674
assert(openedGenericParams.size() == genericParams->size());
1364313675

1364413676
// Match the opened generic parameters to the specialized arguments.
1364513677
auto specializedArgs = type2->castTo<PackType>()->getElementTypes();
1364613678
PackMatcher matcher(openedGenericParams, specializedArgs, getASTContext(),
1364713679
isPackExpansionType);
13648-
if (matcher.match())
13649-
return SolutionKind::Error;
13680+
if (matcher.match()) {
13681+
if (!shouldAttemptFixes())
13682+
return SolutionKind::Error;
13683+
13684+
auto *fix = IgnoreGenericSpecializationArityMismatch::create(
13685+
*this, decl, openedGenericParams.size(), specializedArgs.size(),
13686+
hasParameterPack, getConstraintLocator(locator));
13687+
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
13688+
}
1365013689

1365113690
// Bind the opened generic parameters to the specialization arguments.
1365213691
for (const auto &pair : matcher.pairs) {
@@ -14746,7 +14785,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1474614785
case FixKind::MacroMissingPound:
1474714786
case FixKind::AllowGlobalActorMismatch:
1474814787
case FixKind::AllowAssociatedValueMismatch:
14749-
case FixKind::GenericArgumentsMismatch: {
14788+
case FixKind::GenericArgumentsMismatch:
14789+
case FixKind::AllowConcreteTypeSpecialization:
14790+
case FixKind::IgnoreGenericSpecializationArityMismatch: {
1475014791
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1475114792
}
1475214793
case FixKind::IgnoreInvalidASTNode: {

test/Macros/macro_and_typealias.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// REQUIRES: swift_swift_parser, executable_test
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/variadic_macros.swift -g -no-toolchain-stdlib-rpath
5+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5
6+
7+
@freestanding(expression) public macro Print<each Value>(_ value: repeat each Value) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
8+
@freestanding(expression) public macro OtherPrint<each Value>(_ value: repeat each Value) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
9+
@freestanding(expression) public macro ConcretePrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
10+
@freestanding(expression) public macro MultiPrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
11+
12+
public struct Printer<Value> {
13+
init(_: (Value) -> Void) {}
14+
}
15+
16+
public struct MultiPrinter<T, U> {
17+
// expected-note@-1 {{'T' declared as parameter to type 'MultiPrinter'}}
18+
// expected-note@-2 {{'U' declared as parameter to type 'MultiPrinter'}}
19+
}
20+
21+
typealias Print = Printer
22+
typealias OtherPrint<T> = Printer<T>
23+
typealias ConcretePrint = Printer<Any>
24+
typealias MultiPrint = MultiPrinter
25+
26+
struct Test {
27+
struct Object {
28+
var prop: Int
29+
}
30+
31+
func test() {
32+
let _ = Print<Object> { // Ok
33+
compute(root: $0, \.prop)
34+
}
35+
36+
let _ = Print<Object, Int> {
37+
// expected-error@-1 {{generic type 'Print' specialized with too many type parameters (got 2, but expected 1)}}
38+
}
39+
40+
let _ = OtherPrint<Object> { // Ok
41+
compute(root: $0, \.prop)
42+
}
43+
44+
let _ = ConcretePrint<Object> { // expected-error {{cannot specialize non-generic type 'ConcretePrint' (aka 'Printer<Any>')}}
45+
compute(root: $0, \.prop) // expected-error {{value of type 'Any' has no member 'prop'}}
46+
// expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}}
47+
}
48+
49+
let _ = MultiPrint<Int>()
50+
// expected-error@-1 {{generic type 'MultiPrint' specialized with too few type parameters (got 1, but expected 2)}}
51+
// expected-error@-2 {{generic parameter 'T' could not be inferred}}
52+
// expected-error@-3 {{generic parameter 'U' could not be inferred}}
53+
}
54+
55+
func compute<R, V>(root: R, _: KeyPath<R, V>) {}
56+
}

test/Macros/macros_diagnostics.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ macro genericDeclMacro<T: Numeric, U: Numeric>(_ x: T, _ y: U)
9696
// expected-note @-2 {{where 'U' = 'String'}}
9797

9898
func testDiags(a: Int, b: Int) {
99-
// FIXME: Bad diagnostic.
100-
let s = #stringify<Int, Int>(a + b) // expected-error{{type of expression is ambiguous without a type annotation}}
99+
let s = #stringify<Int, Int>(a + b) // expected-error{{generic type 'stringify' specialized with too many type parameters (got 2, but expected 1)}}
101100

102101
_ = #stringify()
103102
// expected-error@-1{{missing argument for parameter #1 in macro expansion}}

0 commit comments

Comments
 (0)