Skip to content

Commit

Permalink
Merge pull request #75526 from gregomni/specialize
Browse files Browse the repository at this point in the history
[Sema] Improve diagnoses of generic specializations
  • Loading branch information
xedin authored Jul 31, 2024
2 parents 4ce62af + d87e049 commit ceeb6ec
Show file tree
Hide file tree
Showing 19 changed files with 218 additions and 110 deletions.
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

0 comments on commit ceeb6ec

Please sign in to comment.