diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index 4dd625295050b..1cb69a037278b 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -454,6 +454,13 @@ enum class FixKind : uint8_t { /// Ignore the fact that member couldn't be referenced within init accessor /// because its name doesn't appear in 'initializes' or 'accesses' attributes. AllowInvalidMemberReferenceInInitAccessor, + + /// Ignore an attempt to specialize non-generic type. + AllowConcreteTypeSpecialization, + + /// Ignore situations when provided number of generic arguments didn't match + /// expected number of parameters. + IgnoreGenericSpecializationArityMismatch, }; class ConstraintFix { @@ -3647,6 +3654,69 @@ class AllowInvalidMemberReferenceInInitAccessor final : public ConstraintFix { } }; +class AllowConcreteTypeSpecialization final : public ConstraintFix { + Type ConcreteType; + + AllowConcreteTypeSpecialization(ConstraintSystem &cs, Type concreteTy, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AllowConcreteTypeSpecialization, locator), + ConcreteType(concreteTy) {} + +public: + std::string getName() const override { + return "allow concrete type specialization"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + + static AllowConcreteTypeSpecialization * + create(ConstraintSystem &cs, Type concreteTy, ConstraintLocator *locator); + + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AllowConcreteTypeSpecialization; + } +}; + +class IgnoreGenericSpecializationArityMismatch final : public ConstraintFix { + ValueDecl *D; + unsigned NumParams; + unsigned NumArgs; + bool HasParameterPack; + + IgnoreGenericSpecializationArityMismatch(ConstraintSystem &cs, + ValueDecl *decl, unsigned numParams, + unsigned numArgs, + bool hasParameterPack, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::IgnoreGenericSpecializationArityMismatch, + locator), + D(decl), NumParams(numParams), NumArgs(numArgs), + HasParameterPack(hasParameterPack) {} + +public: + std::string getName() const override { + return "ignore generic specialization mismatch"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + + static IgnoreGenericSpecializationArityMismatch * + create(ConstraintSystem &cs, ValueDecl *decl, unsigned numParams, + unsigned numArgs, bool hasParameterPack, ConstraintLocator *locator); + + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::IgnoreGenericSpecializationArityMismatch; + } +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b1531f4a897ba..894254f219b6a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -9162,3 +9162,14 @@ bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() { emitDiagnostic(diag::init_accessor_invalid_member_ref, MemberName); return true; } + +bool ConcreteTypeSpecialization::diagnoseAsError() { + emitDiagnostic(diag::not_a_generic_type, ConcreteType); + return true; +} + +bool InvalidTypeSpecializationArity::diagnoseAsError() { + emitDiagnostic(diag::type_parameter_count_mismatch, D->getBaseIdentifier(), + NumParams, NumArgs, NumArgs < NumParams, HasParameterPack); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index b98436f9760ff..2df238a043ac8 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -3049,6 +3049,50 @@ class InvalidMemberReferenceWithinInitAccessor final bool diagnoseAsError() override; }; +/// Diagnose attempts to specialize a concrete type or its alias: +/// +/// \code +/// struct Test {} +/// typealias X = Test +/// +/// _ = X() // error +/// \endcode +class ConcreteTypeSpecialization final : public FailureDiagnostic { + Type ConcreteType; + +public: + ConcreteTypeSpecialization(const Solution &solution, Type concreteTy, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator), + ConcreteType(resolveType(concreteTy)) {} + + bool diagnoseAsError() override; +}; + +/// Diagnose attempts to specialize with invalid number of generic arguments: +/// +/// \code +/// struct Test {} +/// +/// _ = Test() // error +/// \endcode +class InvalidTypeSpecializationArity final : public FailureDiagnostic { + ValueDecl *D; + unsigned NumParams; + unsigned NumArgs; + bool HasParameterPack; + +public: + InvalidTypeSpecializationArity(const Solution &solution, ValueDecl *decl, + unsigned numParams, unsigned numArgs, + bool hasParameterPack, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator), D(decl), NumParams(numParams), + NumArgs(numArgs), HasParameterPack(hasParameterPack) {} + + bool diagnoseAsError() override; +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index cc9b0775d0b2a..e143897851060 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -2829,3 +2829,34 @@ AllowInvalidMemberReferenceInInitAccessor::create(ConstraintSystem &cs, return new (cs.getAllocator()) AllowInvalidMemberReferenceInInitAccessor(cs, memberName, locator); } + +bool AllowConcreteTypeSpecialization::diagnose(const Solution &solution, + bool asNote) const { + ConcreteTypeSpecialization failure(solution, ConcreteType, getLocator()); + return failure.diagnose(asNote); +} + +AllowConcreteTypeSpecialization * +AllowConcreteTypeSpecialization::create(ConstraintSystem &cs, Type concreteTy, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowConcreteTypeSpecialization(cs, concreteTy, locator); +} + +bool IgnoreGenericSpecializationArityMismatch::diagnose( + const Solution &solution, bool asNote) const { + InvalidTypeSpecializationArity failure(solution, D, NumParams, NumArgs, + HasParameterPack, getLocator()); + return failure.diagnose(asNote); +} + +IgnoreGenericSpecializationArityMismatch * +IgnoreGenericSpecializationArityMismatch::create(ConstraintSystem &cs, + ValueDecl *decl, + unsigned numParams, + unsigned numArgs, + bool hasParameterPack, + ConstraintLocator *locator) { + return new (cs.getAllocator()) IgnoreGenericSpecializationArityMismatch( + cs, decl, numParams, numArgs, hasParameterPack, locator); +} diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9f7545f174a65..299f7974c9a07 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -13595,21 +13595,41 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint( } decl = overloadChoice.getDecl(); + auto openedOverloadTypes = getOpenedTypes(overloadLocator); openedTypes.append(openedOverloadTypes.begin(), openedOverloadTypes.end()); } - auto genericContext = decl->getAsGenericContext(); - if (!genericContext) + std::function getGenericParams = + [&](ValueDecl *decl) -> GenericParamList * { + auto genericContext = decl->getAsGenericContext(); + if (!genericContext) + return nullptr; + + auto genericParams = genericContext->getGenericParams(); + if (!genericParams) { + // If declaration is a non-generic typealias, let's point + // to the underlying generic declaration. + if (auto *TA = dyn_cast(decl)) { + if (auto *UGT = TA->getUnderlyingType()->getAs()) + return getGenericParams(UGT->getDecl()); + } + } + + return genericParams; + }; + + if (!decl->getAsGenericContext()) return SolutionKind::Error; - auto genericParams = genericContext->getGenericParams(); - if (!genericParams || genericParams->size() == 0) { + auto genericParams = getGenericParams(decl); + if (!genericParams) { // FIXME: Record an error here that we're ignoring the parameters. return SolutionKind::Solved; } // Map the generic parameters we have over to their opened types. + bool hasParameterPack = false; SmallVector openedGenericParams; auto genericParamDepth = genericParams->getParams()[0]->getDepth(); for (const auto &openedType : openedTypes) { @@ -13631,19 +13651,38 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint( auto *expansion = PackExpansionType::get(patternType, shapeType); openedGenericParams.push_back(expansion); + hasParameterPack = true; } else { openedGenericParams.push_back(Type(openedType.second)); } } } + + if (openedGenericParams.empty()) { + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + return recordFix(AllowConcreteTypeSpecialization::create( + *this, type1, getConstraintLocator(locator))) + ? SolutionKind::Error + : SolutionKind::Solved; + } + assert(openedGenericParams.size() == genericParams->size()); // Match the opened generic parameters to the specialized arguments. auto specializedArgs = type2->castTo()->getElementTypes(); PackMatcher matcher(openedGenericParams, specializedArgs, getASTContext(), isPackExpansionType); - if (matcher.match()) - return SolutionKind::Error; + if (matcher.match()) { + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + auto *fix = IgnoreGenericSpecializationArityMismatch::create( + *this, decl, openedGenericParams.size(), specializedArgs.size(), + hasParameterPack, getConstraintLocator(locator)); + return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; + } // Bind the opened generic parameters to the specialization arguments. for (const auto &pair : matcher.pairs) { @@ -14743,7 +14782,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::MacroMissingPound: case FixKind::AllowGlobalActorMismatch: case FixKind::AllowAssociatedValueMismatch: - case FixKind::GenericArgumentsMismatch: { + case FixKind::GenericArgumentsMismatch: + case FixKind::AllowConcreteTypeSpecialization: + case FixKind::IgnoreGenericSpecializationArityMismatch: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::IgnoreInvalidASTNode: { diff --git a/test/Macros/macro_and_typealias.swift b/test/Macros/macro_and_typealias.swift new file mode 100644 index 0000000000000..d2c28cd0d8fdb --- /dev/null +++ b/test/Macros/macro_and_typealias.swift @@ -0,0 +1,56 @@ +// REQUIRES: swift_swift_parser, executable_test + +// RUN: %empty-directory(%t) +// 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 +// 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 + +@freestanding(expression) public macro Print(_ value: repeat each Value) = #externalMacro(module: "MacroDefinition", type: "PrintMacro") +@freestanding(expression) public macro OtherPrint(_ value: repeat each Value) = #externalMacro(module: "MacroDefinition", type: "PrintMacro") +@freestanding(expression) public macro ConcretePrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro") +@freestanding(expression) public macro MultiPrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro") + +public struct Printer { + init(_: (Value) -> Void) {} +} + +public struct MultiPrinter { + // expected-note@-1 {{'T' declared as parameter to type 'MultiPrinter'}} + // expected-note@-2 {{'U' declared as parameter to type 'MultiPrinter'}} +} + +typealias Print = Printer +typealias OtherPrint = Printer +typealias ConcretePrint = Printer +typealias MultiPrint = MultiPrinter + +struct Test { + struct Object { + var prop: Int + } + + func test() { + let _ = Print { // Ok + compute(root: $0, \.prop) + } + + let _ = Print { + // expected-error@-1 {{generic type 'Print' specialized with too many type parameters (got 2, but expected 1)}} + } + + let _ = OtherPrint { // Ok + compute(root: $0, \.prop) + } + + let _ = ConcretePrint { // expected-error {{cannot specialize non-generic type 'ConcretePrint' (aka 'Printer')}} + compute(root: $0, \.prop) // expected-error {{value of type 'Any' has no member 'prop'}} + // expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + } + + let _ = MultiPrint() + // expected-error@-1 {{generic type 'MultiPrint' specialized with too few type parameters (got 1, but expected 2)}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} + // expected-error@-3 {{generic parameter 'U' could not be inferred}} + } + + func compute(root: R, _: KeyPath) {} +} diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 55c7d46ed2e34..e63bde5ec4646 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -96,8 +96,7 @@ macro genericDeclMacro(_ x: T, _ y: U) // expected-note @-2 {{where 'U' = 'String'}} func testDiags(a: Int, b: Int) { - // FIXME: Bad diagnostic. - let s = #stringify(a + b) // expected-error{{type of expression is ambiguous without a type annotation}} + let s = #stringify(a + b) // expected-error{{generic type 'stringify' specialized with too many type parameters (got 2, but expected 1)}} _ = #stringify() // expected-error@-1{{missing argument for parameter #1 in macro expansion}}