Skip to content
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
2 changes: 0 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4628,8 +4628,6 @@ NOTE(duplicated_key_declared_here, none,
// Generic specializations
ERROR(cannot_explicitly_specialize_generic_function,none,
"cannot explicitly specialize a generic function", ())
ERROR(not_a_generic_definition,none,
"cannot specialize a non-generic definition", ())
ERROR(not_a_generic_type,none,
"cannot specialize non-generic type %0", (Type))
ERROR(protocol_declares_unknown_primary_assoc_type,none,
Expand Down
32 changes: 31 additions & 1 deletion include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,12 @@ enum class FixKind : uint8_t {
/// because its name doesn't appear in 'initializes' or 'accesses' attributes.
AllowInvalidMemberReferenceInInitAccessor,

/// Ignore an attempt to specialize non-generic type.
/// Ignore an attempt to specialize a non-generic type.
AllowConcreteTypeSpecialization,

/// Ignore an attempt to specialize a generic function.
AllowGenericFunctionSpecialization,

/// Ignore an out-of-place \c then statement.
IgnoreOutOfPlaceThenStmt,

Expand Down Expand Up @@ -3713,6 +3716,33 @@ class AllowConcreteTypeSpecialization final : public ConstraintFix {
}
};

class AllowGenericFunctionSpecialization final : public ConstraintFix {
ValueDecl *Decl;

AllowGenericFunctionSpecialization(ConstraintSystem &cs, ValueDecl *decl,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowConcreteTypeSpecialization, locator),
Decl(decl) {}

public:
std::string getName() const override {
return "allow generic function specialization";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}

static AllowGenericFunctionSpecialization *
create(ConstraintSystem &cs, ValueDecl *decl, ConstraintLocator *locator);

static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::AllowGenericFunctionSpecialization;
}
};

class IgnoreOutOfPlaceThenStmt final : public ConstraintFix {
IgnoreOutOfPlaceThenStmt(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::IgnoreOutOfPlaceThenStmt, locator) {}
Expand Down
4 changes: 3 additions & 1 deletion lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,9 @@ static bool isGenericTypeDisambiguatingToken(Parser &P) {
auto &tok = P.Tok;
switch (tok.getKind()) {
default:
return false;
// If this is the end of the expr (wouldn't match parseExprSequenceElement),
// prefer generic type list over an illegal unary postfix '>' operator.
return P.isStartOfSwiftDecl() || P.isStartOfStmt(/*prefer expr=*/true);
case tok::r_paren:
case tok::r_square:
case tok::l_brace:
Expand Down
7 changes: 6 additions & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9338,7 +9338,12 @@ bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() {
}

bool ConcreteTypeSpecialization::diagnoseAsError() {
emitDiagnostic(diag::not_a_generic_type, ConcreteType);
emitDiagnostic(diag::not_a_generic_type, resolveType(ConcreteType));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd prefer the resolveType happened during construction of the diagnostic, it's usually safer that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. That was an accidental leftover from the changes along the way.

return true;
}

bool GenericFunctionSpecialization::diagnoseAsError() {
emitDiagnostic(diag::cannot_explicitly_specialize_generic_function);
return true;
}

Expand Down
13 changes: 12 additions & 1 deletion lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3116,7 +3116,18 @@ class ConcreteTypeSpecialization final : public FailureDiagnostic {
ConcreteTypeSpecialization(const Solution &solution, Type concreteTy,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator),
ConcreteType(resolveType(concreteTy)) {}
ConcreteType(concreteTy) {}

bool diagnoseAsError() override;
};

class GenericFunctionSpecialization final : public FailureDiagnostic {
ValueDecl *Decl;

public:
GenericFunctionSpecialization(const Solution &solution, ValueDecl *decl,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator), Decl(decl) {}

bool diagnoseAsError() override;
};
Expand Down
12 changes: 12 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,18 @@ AllowConcreteTypeSpecialization::create(ConstraintSystem &cs, Type concreteTy,
AllowConcreteTypeSpecialization(cs, concreteTy, locator);
}

bool AllowGenericFunctionSpecialization::diagnose(const Solution &solution,
bool asNote) const {
GenericFunctionSpecialization failure(solution, Decl, getLocator());
return failure.diagnose(asNote);
}

AllowGenericFunctionSpecialization *AllowGenericFunctionSpecialization::create(
ConstraintSystem &cs, ValueDecl *decl, ConstraintLocator *locator) {
return new (cs.getAllocator())
AllowGenericFunctionSpecialization(cs, decl, locator);
}

bool IgnoreOutOfPlaceThenStmt::diagnose(const Solution &solution,
bool asNote) const {
OutOfPlaceThenStmtFailure failure(solution, getLocator());
Expand Down
74 changes: 24 additions & 50 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1903,9 +1903,9 @@ namespace {
/// introduce them as an explicit generic arguments constraint.
///
/// \returns true if resolving any of the specialization types failed.
bool addSpecializationConstraint(
ConstraintLocator *locator, Type boundType,
ArrayRef<TypeRepr *> specializationArgs) {
void addSpecializationConstraint(ConstraintLocator *locator, Type boundType,
SourceLoc lAngleLoc,
ArrayRef<TypeRepr *> specializationArgs) {
// Resolve each type.
SmallVector<Type, 2> specializationArgTypes;
auto options =
Expand All @@ -1916,61 +1916,36 @@ namespace {
options |= TypeResolutionFlags::AllowPackReferences;
elementEnv = OuterExpansions.back();
}
const auto result = TypeResolution::resolveContextualType(
auto result = TypeResolution::resolveContextualType(
specializationArg, CurDC, options,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, locator),
HandlePlaceholderType(CS, locator),
OpenPackElementType(CS, locator, elementEnv));
if (result->hasError())
return true;

if (result->hasError()) {
auto &ctxt = CS.getASTContext();
auto *repr = new (ctxt) PlaceholderTypeRepr(specializationArg->getLoc());
result = PlaceholderType::get(ctxt, repr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of create a placeholder repr placeholder type should use original specializationArg

Copy link
Contributor Author

@gregomni gregomni Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PlaceholderType Originator argument requires specifically a PlaceholderTypeRepr, although the uses don't seem to require anything specific to that subclass, except that LocatorPathElt::PlaceholderType also requires it. So I could change both of those types to accept any TypeRepr. Is that what you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like replaceInferableTypesWithTypeVars does this same pattern of creating a PlaceholderTypeRepr, so I think this should be left as is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That method is actually going the other way - it detects if there are placeholders in a Type and "opens" them. I think we should expand from PlaceholderTypeRepr to TypeRepr in PlaceholderType and locator element. But we can do that in a separate PR.

ctxt.Diags.diagnose(lAngleLoc,
diag::while_parsing_as_left_angle_bracket);
}
specializationArgTypes.push_back(result);
}

CS.addConstraint(
ConstraintKind::ExplicitGenericArguments, boundType,
PackType::get(CS.getASTContext(), specializationArgTypes),
locator);
return false;
auto constraint = Constraint::create(
CS, ConstraintKind::ExplicitGenericArguments, boundType,
PackType::get(CS.getASTContext(), specializationArgTypes), locator);
CS.addUnsolvedConstraint(constraint);
CS.activateConstraint(constraint);
}

Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
auto baseTy = CS.getType(expr->getSubExpr());

if (baseTy->isTypeVariableOrMember()) {
return baseTy;
}

// We currently only support explicit specialization of generic types.
// FIXME: We could support explicit function specialization.
auto &de = CS.getASTContext().Diags;
if (baseTy->is<AnyFunctionType>()) {
de.diagnose(expr->getSubExpr()->getLoc(),
diag::cannot_explicitly_specialize_generic_function);
de.diagnose(expr->getLAngleLoc(),
diag::while_parsing_as_left_angle_bracket);
return Type();
}

if (AnyMetatypeType *meta = baseTy->getAs<AnyMetatypeType>()) {
auto *overloadLocator = CS.getConstraintLocator(expr->getSubExpr());
if (addSpecializationConstraint(overloadLocator,
meta->getInstanceType(),
expr->getUnresolvedParams())) {
return Type();
}

return baseTy;
}

// FIXME: If the base type is a type variable, constrain it to a metatype
// of a bound generic type.
de.diagnose(expr->getSubExpr()->getLoc(),
diag::not_a_generic_definition);
de.diagnose(expr->getLAngleLoc(),
diag::while_parsing_as_left_angle_bracket);
return Type();
auto *overloadLocator = CS.getConstraintLocator(expr->getSubExpr());
addSpecializationConstraint(
overloadLocator, baseTy->getMetatypeInstanceType(),
expr->getLAngleLoc(), expr->getUnresolvedParams());
return baseTy;
}

Type visitSequenceExpr(SequenceExpr *expr) {
Expand Down Expand Up @@ -4082,10 +4057,9 @@ namespace {

// Add explicit generic arguments, if there were any.
if (expr->getGenericArgsRange().isValid()) {
if (addSpecializationConstraint(
CS.getConstraintLocator(expr), macroRefType,
expr->getGenericArgs()))
return Type();
addSpecializationConstraint(CS.getConstraintLocator(expr), macroRefType,
expr->getGenericArgsRange().Start,
expr->getGenericArgs());
}

// Form the applicable-function constraint. The result type
Expand Down
22 changes: 13 additions & 9 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13926,6 +13926,8 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(

// Bail out if we haven't selected an overload yet.
auto simplifiedBoundType = simplifyType(type1, flags);
if (simplifiedBoundType->isPlaceholder())
return SolutionKind::Solved;
if (simplifiedBoundType->isTypeVariableOrMember())
return formUnsolved();

Expand Down Expand Up @@ -14018,13 +14020,12 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
}
}

if (!decl->getAsGenericContext())
return SolutionKind::Error;

auto genericParams = getGenericParams(decl);
if (!genericParams) {
// FIXME: Record an error here that we're ignoring the parameters.
return SolutionKind::Solved;
if (!decl->getAsGenericContext() || !genericParams) {
return recordFix(AllowConcreteTypeSpecialization::create(
*this, type1, getConstraintLocator(locator)))
? SolutionKind::Error
: SolutionKind::Solved;
}

// Map the generic parameters we have over to their opened types.
Expand Down Expand Up @@ -14057,12 +14058,14 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
}
}

if (openedGenericParams.empty()) {
// FIXME: We could support explicit function specialization.
if (openedGenericParams.empty() ||
(isa<AbstractFunctionDecl>(decl) && !hasParameterPack)) {
if (!shouldAttemptFixes())
return SolutionKind::Error;

return recordFix(AllowConcreteTypeSpecialization::create(
*this, type1, getConstraintLocator(locator)))
return recordFix(AllowGenericFunctionSpecialization::create(
*this, decl, getConstraintLocator(locator)))
? SolutionKind::Error
: SolutionKind::Solved;
}
Expand Down Expand Up @@ -15193,6 +15196,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
case FixKind::AllowAssociatedValueMismatch:
case FixKind::GenericArgumentsMismatch:
case FixKind::AllowConcreteTypeSpecialization:
case FixKind::AllowGenericFunctionSpecialization:
case FixKind::IgnoreGenericSpecializationArityMismatch:
case FixKind::IgnoreKeyPathSubscriptIndexMismatch:
case FixKind::AllowMemberRefOnExistential: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ func test() {

S<Int>(t: 42).test() // expected-error {{ambiguous use of 'init(t:)'}}

// FIXME(diagnostics): This should produce ambiguity diagnostic too
S<Int>.staticFn()
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
// expected-error@-1 {{ambiguous use of 'staticFn()'}}
}
14 changes: 10 additions & 4 deletions test/Parse/enum_element_pattern_swift4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ enum E {

static func testE(e: E) {
switch e {
case A<UndefinedTy>(): // expected-error {{cannot specialize a non-generic definition}}
case A<UndefinedTy>(): // expected-error {{cannot find type 'UndefinedTy' in scope}}
// expected-note@-1 {{while parsing this '<' as a type parameter bracket}}
// expected-error@-2 {{cannot specialize non-generic type 'E'}}
// expected-error@-3 {{cannot call value of non-function type 'E'}}
break
case B<Int>(): // expected-error {{cannot specialize a non-generic definition}} expected-note {{while parsing this '<' as a type parameter bracket}}
case B<Int>(): // expected-error {{cannot specialize non-generic type 'E'}}
// expected-error@-1 {{cannot call value of non-function type 'E'}}
break
default:
break;
Expand All @@ -22,10 +25,13 @@ enum E {

func testE(e: E) {
switch e {
case E.A<UndefinedTy>(): // expected-error {{cannot specialize a non-generic definition}}
case E.A<UndefinedTy>(): // expected-error {{cannot find type 'UndefinedTy' in scope}}
// expected-note@-1 {{while parsing this '<' as a type parameter bracket}}
// expected-error@-2 {{cannot specialize non-generic type 'E'}}
// expected-error@-3 {{cannot call value of non-function type 'E'}}
break
case E.B<Int>(): // expected-error {{cannot specialize a non-generic definition}} expected-note {{while parsing this '<' as a type parameter bracket}}
case E.B<Int>(): // expected-error {{cannot specialize non-generic type 'E'}}
// expected-error@-1 {{cannot call value of non-function type 'E'}}
break
case .C(): // expected-error {{pattern with associated values does not match enum case 'C'}}
// expected-note@-1 {{remove associated values to make the pattern match}} {{10-12=}}
Expand Down
16 changes: 11 additions & 5 deletions test/Parse/generic_disambiguation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,21 @@ var a, b, c, d : Int
_ = a < b
_ = (a < b, c > d)
// Parses as generic because of lparen after '>'
(a < b, c > (d)) // expected-error{{cannot specialize a non-generic definition}}
// expected-note@-1 {{while parsing this '<' as a type parameter bracket}}
(a < b, c > (d)) // expected-error{{cannot find type 'b' in scope}}
// expected-note@-1 2 {{while parsing this '<' as a type parameter bracket}}
// expected-error@-2 {{cannot specialize non-generic type 'Int'}}
// expected-error@-3 {{cannot call value of non-function type 'Int'}}
// expected-error@-4 {{cannot find type 'c' in scope}}
// Parses as generic because of lparen after '>'
(a<b, c>(d)) // expected-error{{cannot specialize a non-generic definition}}
// expected-note@-1 {{while parsing this '<' as a type parameter bracket}}
(a<b, c>(d)) // expected-error{{cannot find type 'b' in scope}}
// expected-note@-1 2 {{while parsing this '<' as a type parameter bracket}}
// expected-error@-2 {{cannot specialize non-generic type 'Int'}}
// expected-error@-3 {{cannot call value of non-function type 'Int'}}
// expected-error@-4 {{cannot find type 'c' in scope}}
_ = a>(b)
_ = a > (b)

generic<Int>(0) // expected-error{{cannot explicitly specialize a generic function}} expected-note{{while parsing this '<' as a type parameter bracket}}
generic<Int>(0) // expected-error{{cannot explicitly specialize a generic function}}

A<B>.c()
A<A<B>>.c()
Expand Down
41 changes: 41 additions & 0 deletions test/Sema/generic-arg-list.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: %target-typecheck-verify-swift

extension Int {
func foo() -> Int {}
var bar: Int {
get {}
}

func baz() -> Int {}
func baz(_ x: Int = 0) -> Int {}

func gen<T>() -> T {} // expected-note 2 {{in call to function 'gen()'}}
}

// https://github.com/swiftlang/swift/issues/74857
func test(i: Int) {
let _ = i.foo<Int>() // expected-error {{cannot specialize non-generic type '() -> Int'}}

let _ = i.gen<Int>() // expected-error {{cannot explicitly specialize a generic function}}
// expected-error@-1 {{generic parameter 'T' could not be inferred}}

let _ = 0.foo<Int>() // expected-error {{cannot specialize non-generic type '() -> Int'}}

let _ = i.gen<Int> // expected-error {{cannot explicitly specialize a generic function}}
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
let _ = i.bar<Int> // expected-error {{cannot specialize non-generic type 'Int'}}
let _ = 0.bar<Int> // expected-error {{cannot specialize non-generic type 'Int'}}
}

extension Bool {
func foo<T>() -> T {}
}

let _: () -> Bool = false.foo<Int> // expected-error {{cannot explicitly specialize a generic function}}

func foo(_ x: Int) {
_ = {
_ = x<String> // expected-error {{cannot specialize non-generic type 'Int'}}
}
}