Skip to content

[5.9][ConstraintSystem] Fix a couple of issues related to generic specialization #67026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand Down
11 changes: 11 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9101,3 +9101,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;
}
44 changes: 44 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3036,6 +3036,50 @@ class InvalidMemberReferenceWithinInitAccessor final
bool diagnoseAsError() override;
};

/// Diagnose attempts to specialize a concrete type or its alias:
///
/// \code
/// struct Test {}
/// typealias X = Test
///
/// _ = X<Int>() // 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<T, U> {}
///
/// _ = Test<Int>() // 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

Expand Down
31 changes: 31 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2837,3 +2837,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);
}
55 changes: 48 additions & 7 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13598,21 +13598,41 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
}

decl = overloadChoice.getDecl();

auto openedOverloadTypes = getOpenedTypes(overloadLocator);
openedTypes.append(openedOverloadTypes.begin(), openedOverloadTypes.end());
}

auto genericContext = decl->getAsGenericContext();
if (!genericContext)
std::function<GenericParamList *(ValueDecl *)> 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<TypeAliasDecl>(decl)) {
if (auto *UGT = TA->getUnderlyingType()->getAs<AnyGenericType>())
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<Type, 2> openedGenericParams;
auto genericParamDepth = genericParams->getParams()[0]->getDepth();
for (const auto &openedType : openedTypes) {
Expand All @@ -13634,19 +13654,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<PackType>()->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) {
Expand Down Expand Up @@ -14746,7 +14785,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: {
Expand Down
56 changes: 56 additions & 0 deletions test/Macros/macro_and_typealias.swift
Original file line number Diff line number Diff line change
@@ -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<each Value>(_ value: repeat each Value) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
@freestanding(expression) public macro OtherPrint<each Value>(_ 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<Value> {
init(_: (Value) -> Void) {}
}

public struct MultiPrinter<T, U> {
// 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<T> = Printer<T>
typealias ConcretePrint = Printer<Any>
typealias MultiPrint = MultiPrinter

struct Test {
struct Object {
var prop: Int
}

func test() {
let _ = Print<Object> { // Ok
compute(root: $0, \.prop)
}

let _ = Print<Object, Int> {
// expected-error@-1 {{generic type 'Print' specialized with too many type parameters (got 2, but expected 1)}}
}

let _ = OtherPrint<Object> { // Ok
compute(root: $0, \.prop)
}

let _ = ConcretePrint<Object> { // expected-error {{cannot specialize non-generic type 'ConcretePrint' (aka 'Printer<Any>')}}
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<Int>()
// 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<R, V>(root: R, _: KeyPath<R, V>) {}
}
3 changes: 1 addition & 2 deletions test/Macros/macros_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ macro genericDeclMacro<T: Numeric, U: Numeric>(_ x: T, _ y: U)
// expected-note @-2 {{where 'U' = 'String'}}

func testDiags(a: Int, b: Int) {
// FIXME: Bad diagnostic.
let s = #stringify<Int, Int>(a + b) // expected-error{{type of expression is ambiguous without a type annotation}}
let s = #stringify<Int, Int>(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}}
Expand Down