Skip to content

[Sema] Improve diagnoses of generic specializations #75526

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
Jul 31, 2024
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
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4605,10 +4605,10 @@ 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(not_a_generic_macro,none,
"cannot specialize a non-generic external macro %0", (const ValueDecl *))
ERROR(protocol_declares_unknown_primary_assoc_type,none,
"an associated type named %0 must be declared in the protocol %1 or a protocol it inherits",
(Identifier, Type))
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -7327,7 +7327,7 @@ class PlaceholderType : public TypeBase {
// recursive property logic in PlaceholderType::get.
using Originator =
llvm::PointerUnion<TypeVariableType *, DependentMemberType *, VarDecl *,
ErrorExpr *, PlaceholderTypeRepr *>;
ErrorExpr *, TypeRepr *>;

Originator O;

Expand Down
44 changes: 39 additions & 5 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,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 @@ -3718,11 +3721,14 @@ class AllowInvalidMemberReferenceInInitAccessor final : public ConstraintFix {

class AllowConcreteTypeSpecialization final : public ConstraintFix {
Type ConcreteType;
ValueDecl *Decl;

AllowConcreteTypeSpecialization(ConstraintSystem &cs, Type concreteTy,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowConcreteTypeSpecialization, locator),
ConcreteType(concreteTy) {}
ValueDecl *decl, ConstraintLocator *locator,
FixBehavior fixBehavior)
: ConstraintFix(cs, FixKind::AllowConcreteTypeSpecialization, locator,
fixBehavior),
ConcreteType(concreteTy), Decl(decl) {}

public:
std::string getName() const override {
Expand All @@ -3736,13 +3742,41 @@ class AllowConcreteTypeSpecialization final : public ConstraintFix {
}

static AllowConcreteTypeSpecialization *
create(ConstraintSystem &cs, Type concreteTy, ConstraintLocator *locator);
create(ConstraintSystem &cs, Type concreteTy, ValueDecl *decl,
ConstraintLocator *locator, FixBehavior fixBehavior);

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

class AllowGenericFunctionSpecialization final : public ConstraintFix {
ValueDecl *Decl;

AllowGenericFunctionSpecialization(ConstraintSystem &cs, ValueDecl *decl,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowGenericFunctionSpecialization, 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
6 changes: 3 additions & 3 deletions include/swift/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -985,13 +985,13 @@ class LocatorPathElt::ConformanceRequirement final
};

class LocatorPathElt::PlaceholderType final
: public StoredPointerElement<PlaceholderTypeRepr> {
: public StoredPointerElement<TypeRepr> {
public:
PlaceholderType(PlaceholderTypeRepr *placeholderRepr)
PlaceholderType(TypeRepr *placeholderRepr)
: StoredPointerElement(PathElementKind::PlaceholderType,
placeholderRepr) {}

PlaceholderTypeRepr *getPlaceholderRepr() const { return getStoredPointer(); }
TypeRepr *getPlaceholderRepr() const { return getStoredPointer(); }

static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::PlaceholderType;
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3987,8 +3987,8 @@ namespace {
printFlag("error_expr");
} else if (auto *DMT = originator.dyn_cast<DependentMemberType *>()) {
printRec(DMT, "dependent_member_type");
} else if (originator.is<PlaceholderTypeRepr *>()) {
printFlag("placeholder_type_repr");
} else if (originator.is<TypeRepr *>()) {
printFlag("type_repr");
} else {
assert(false && "unknown originator");
}
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5870,8 +5870,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
Printer << "error_expr";
} else if (auto *DMT = originator.dyn_cast<DependentMemberType *>()) {
visit(DMT);
} else if (originator.is<PlaceholderTypeRepr *>()) {
Printer << "placeholder_type_repr";
} else if (originator.is<TypeRepr *>()) {
Printer << "type_repr";
} else {
assert(false && "unknown originator");
}
Expand Down
4 changes: 3 additions & 1 deletion lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,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
11 changes: 10 additions & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9360,7 +9360,16 @@ bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() {
}

bool ConcreteTypeSpecialization::diagnoseAsError() {
emitDiagnostic(diag::not_a_generic_type, ConcreteType);
if (isa<MacroDecl>(Decl)) {
emitDiagnostic(diag::not_a_generic_macro, Decl);
} else {
emitDiagnostic(diag::not_a_generic_type, ConcreteType);
}
return true;
}

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

Expand Down
19 changes: 16 additions & 3 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3127,12 +3127,25 @@ class InvalidMemberReferenceWithinInitAccessor final
/// \endcode
class ConcreteTypeSpecialization final : public FailureDiagnostic {
Type ConcreteType;
ValueDecl *Decl;

public:
ConcreteTypeSpecialization(const Solution &solution, Type concreteTy,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator),
ConcreteType(resolveType(concreteTy)) {}
ValueDecl *decl, ConstraintLocator *locator,
FixBehavior fixBehavior)
: FailureDiagnostic(solution, locator, fixBehavior),
ConcreteType(resolveType(concreteTy)), Decl(decl) {}

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
23 changes: 18 additions & 5 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2605,15 +2605,28 @@ AllowInvalidMemberReferenceInInitAccessor::create(ConstraintSystem &cs,

bool AllowConcreteTypeSpecialization::diagnose(const Solution &solution,
bool asNote) const {
ConcreteTypeSpecialization failure(solution, ConcreteType, getLocator());
ConcreteTypeSpecialization failure(solution, ConcreteType, Decl, getLocator(),
fixBehavior);
return failure.diagnose(asNote);
}

AllowConcreteTypeSpecialization *
AllowConcreteTypeSpecialization::create(ConstraintSystem &cs, Type concreteTy,
ConstraintLocator *locator) {
AllowConcreteTypeSpecialization *AllowConcreteTypeSpecialization::create(
ConstraintSystem &cs, Type concreteTy, ValueDecl *decl,
ConstraintLocator *locator, FixBehavior fixBehavior) {
return new (cs.getAllocator()) AllowConcreteTypeSpecialization(
cs, concreteTy, decl, locator, fixBehavior);
}

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())
AllowConcreteTypeSpecialization(cs, concreteTy, locator);
AllowGenericFunctionSpecialization(cs, decl, locator);
}

bool IgnoreOutOfPlaceThenStmt::diagnose(const Solution &solution,
Expand Down
79 changes: 26 additions & 53 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,35 @@ 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();
result = PlaceholderType::get(ctxt, specializationArg);
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 @@ -4080,10 +4054,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 Expand Up @@ -4828,11 +4801,11 @@ bool ConstraintSystem::generateConstraints(
// If we have a placeholder originating from a PlaceholderTypeRepr,
// tack that on to the locator.
if (auto *placeholderTy = ty->getAs<PlaceholderType>())
if (auto *placeholderRepr = placeholderTy->getOriginator()
.dyn_cast<PlaceholderTypeRepr *>())
if (auto *typeRepr = placeholderTy->getOriginator()
.dyn_cast<TypeRepr *>())
return getConstraintLocator(
convertTypeLocator,
LocatorPathElt::PlaceholderType(placeholderRepr));
LocatorPathElt::PlaceholderType(typeRepr));
return convertTypeLocator;
};

Expand Down
Loading