Skip to content

[5.9🍒] Be more specific than "noncopyable type T can't be used with generics yet" #66946

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 6 commits into from
Jun 27, 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
32 changes: 30 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5448,7 +5448,9 @@ ERROR(tuple_pack_element_label,none,
"cannot use label with pack expansion tuple element",
())
ERROR(tuple_move_only_not_supported,none,
"tuples with noncopyable elements are not supported", ())
"tuple with noncopyable element type %0 is not supported", (Type))
ERROR(tuple_containing_move_only_not_supported,none,
"type %0 containing noncopyable element is not supported", (Type))
ERROR(vararg_not_allowed,none,
"variadic parameter cannot appear outside of a function parameter list",
())
Expand Down Expand Up @@ -7195,7 +7197,33 @@ ERROR(self_ownership_specifier_copyable,none,
(SelfAccessKind, DescriptiveDeclKind))
ERROR(ownership_specifier_nonescaping_closure,none,
"'%0' cannot be applied to nonescaping closure", (StringRef))
ERROR(noncopyable_generics, none, "noncopyable type %0 cannot be used with generics yet", (Type))
ERROR(noncopyable_generics, none,
"noncopyable type %0 cannot be used with generics yet",
(Type))
ERROR(noncopyable_generics_variadic, none,
"noncopyable type %0 cannot be used within a variadic type yet",
(Type))
ERROR(noncopyable_generics_specific, none,
"noncopyable type %0 cannot be used with generic type %1 yet",
(Type, Type))
ERROR(noncopyable_generics_erasure, none,
"noncopyable type %0 cannot be erased to copyable existential type %1",
(Type, Type))
ERROR(noncopyable_generics_metatype_cast, none,
"metatype %0 cannot be cast to %1 because %2 is noncopyable",
(Type, Type, Type))
ERROR(noncopyable_generics_generic_param, none,
"noncopyable type %0 cannot be substituted for copyable %1 %2 in %3",
(Type, DescriptiveDeclKind, Type, DeclName))
ERROR(noncopyable_generics_generic_param_metatype, none,
"metatype %4 of noncopyable type %0 cannot be substituted for copyable %1 %2 in %3",
(Type, DescriptiveDeclKind, Type, DeclName, Type))
NOTE(noncopyable_generics_implicit_copyable, none,
"%0 %1 has an implicit Copyable requirement",
(DescriptiveDeclKind, Type))
ERROR(noncopyable_element_of_pack_not_supported,none,
"parameter pack containing noncopyable element %0 is not supported",
(Type))
ERROR(noncopyable_effectful_getter,none,
"%0 of noncopyable type cannot be 'async' or 'throws'", (DescriptiveDeclKind))
ERROR(noncopyable_enums_do_not_support_indirect,none,
Expand Down
46 changes: 45 additions & 1 deletion include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -2082,10 +2082,53 @@ class NotCompileTimeConst final : public ContextualMismatch {
}
};

/// Describes the reason why the type must be copyable
struct NoncopyableMatchFailure {
enum Kind {
CopyableConstraint,
ExistentialCast,
};

private:
Kind reason;
union {
Type type;
};

NoncopyableMatchFailure(Kind reason, Type type)
: reason(reason), type(type) {}

public:
Kind getKind() const { return reason; }

Type getType() const {
switch (reason) {
case ExistentialCast:
return type;

case CopyableConstraint:
llvm_unreachable("no type payload");
};
}

static NoncopyableMatchFailure forCopyableConstraint() {
return NoncopyableMatchFailure(CopyableConstraint, Type());
}

static NoncopyableMatchFailure forExistentialCast(Type existential) {
assert(existential->isAnyExistentialType());
return NoncopyableMatchFailure(ExistentialCast, existential);
}
};

class MustBeCopyable final : public ConstraintFix {
Type noncopyableTy;
NoncopyableMatchFailure failure;

MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator);
MustBeCopyable(ConstraintSystem &cs,
Type noncopyableTy,
NoncopyableMatchFailure failure,
ConstraintLocator *locator);

public:
std::string getName() const override { return "remove move-only from type"; }
Expand All @@ -2096,6 +2139,7 @@ class MustBeCopyable final : public ConstraintFix {

static MustBeCopyable *create(ConstraintSystem &cs,
Type noncopyableTy,
NoncopyableMatchFailure failure,
ConstraintLocator *locator);

static bool classof(const ConstraintFix *fix) {
Expand Down
66 changes: 66 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6095,6 +6095,72 @@ bool NotCompileTimeConstFailure::diagnoseAsError() {
}

bool NotCopyableFailure::diagnoseAsError() {
switch (failure.getKind()) {
case NoncopyableMatchFailure::ExistentialCast: {
if (noncopyableTy->is<AnyMetatypeType>())
emitDiagnostic(diag::noncopyable_generics_metatype_cast,
noncopyableTy,
failure.getType(),
noncopyableTy->getMetatypeInstanceType());
else
emitDiagnostic(diag::noncopyable_generics_erasure,
noncopyableTy,
failure.getType());
return true;
}

case NoncopyableMatchFailure::CopyableConstraint: {
auto *loc = getLocator();

if (loc->isLastElement<LocatorPathElt::AnyTupleElement>()) {
assert(!noncopyableTy->is<TupleType>() && "will use poor wording");
emitDiagnostic(diag::tuple_move_only_not_supported, noncopyableTy);
return true;
}

if (loc->isLastElement<LocatorPathElt::PackElement>()) {
emitDiagnostic(diag::noncopyable_element_of_pack_not_supported,
noncopyableTy);
return true;
}

// a bit paranoid of nulls and such...
if (auto *genericParam = loc->getGenericParameter()) {
if (auto *paramDecl = genericParam->getDecl()) {
if (auto *owningDecl =
dyn_cast_or_null<ValueDecl>(paramDecl->getDeclContext()->getAsDecl())) {

// FIXME: these owningDecl names are kinda bad. like just `init(describing:)`
if (noncopyableTy->is<AnyMetatypeType>())
emitDiagnostic(diag::noncopyable_generics_generic_param_metatype,
noncopyableTy->getMetatypeInstanceType(),
paramDecl->getDescriptiveKind(),
genericParam,
owningDecl->getName(),
noncopyableTy);
else
emitDiagnostic(diag::noncopyable_generics_generic_param,
noncopyableTy,
paramDecl->getDescriptiveKind(),
genericParam,
owningDecl->getName());

// If we have a location for the parameter, point it out in a note.
if (auto loc = paramDecl->getNameLoc()) {
emitDiagnosticAt(loc,
diag::noncopyable_generics_implicit_copyable,
paramDecl->getDescriptiveKind(),
genericParam);
}

return true;
}
}
}
break;
}
}

emitDiagnostic(diag::noncopyable_generics, noncopyableTy);
return true;
}
Expand Down
17 changes: 11 additions & 6 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,16 +539,16 @@ class MemberAccessOnOptionalBaseFailure final : public MemberReferenceFailure {
ResultTypeIsOptional(resultOptional) {}

bool diagnoseAsError() override;

Type getMemberBaseType() const {
return MemberBaseType;
}

SourceLoc getLoc() const override {
// The end location points to the dot in the member access.
return getSourceRange().End;
}

SourceRange getSourceRange() const override;

};
Expand Down Expand Up @@ -670,7 +670,7 @@ class ContextualFailure : public FailureDiagnostic {

/// If we're trying to convert something to `nil`.
bool diagnoseConversionToNil() const;

/// Diagnose failed conversion in a `CoerceExpr`.
bool diagnoseCoercionToUnrelatedType() const;

Expand Down Expand Up @@ -1816,9 +1816,14 @@ class NotCompileTimeConstFailure final : public FailureDiagnostic {

class NotCopyableFailure final : public FailureDiagnostic {
Type noncopyableTy;
NoncopyableMatchFailure failure;
public:
NotCopyableFailure(const Solution &solution, Type noncopyableTy, ConstraintLocator *locator)
: FailureDiagnostic(solution, locator), noncopyableTy(noncopyableTy) {}
NotCopyableFailure(const Solution &solution,
Type noncopyableTy,
NoncopyableMatchFailure failure,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator),
noncopyableTy(noncopyableTy), failure(failure) {}

bool diagnoseAsError() override;
};
Expand Down
15 changes: 10 additions & 5 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1368,19 +1368,24 @@ bool NotCompileTimeConst::diagnose(const Solution &solution, bool asNote) const
return failure.diagnose(asNote);
}

MustBeCopyable::MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator)
MustBeCopyable::MustBeCopyable(ConstraintSystem &cs,
Type noncopyableTy,
NoncopyableMatchFailure failure,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::MustBeCopyable, locator, FixBehavior::Error),
noncopyableTy(noncopyableTy) {}
noncopyableTy(noncopyableTy), failure(failure) {}

bool MustBeCopyable::diagnose(const Solution &solution, bool asNote) const {
NotCopyableFailure failure(solution, noncopyableTy, getLocator());
return failure.diagnose(asNote);
NotCopyableFailure failDiag(solution, noncopyableTy, failure, getLocator());
return failDiag.diagnose(asNote);
}

MustBeCopyable* MustBeCopyable::create(ConstraintSystem &cs,
Type noncopyableTy,
NoncopyableMatchFailure failure,
ConstraintLocator *locator) {
return new (cs.getAllocator()) MustBeCopyable(cs, noncopyableTy, locator);
return new (cs.getAllocator()) MustBeCopyable(cs, noncopyableTy,
failure, locator);
}

bool MustBeCopyable::diagnoseForAmbiguity(CommonFixesArray commonFixes) const {
Expand Down
12 changes: 9 additions & 3 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3832,8 +3832,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
if (type1->getMetatypeInstanceType()->isPureMoveOnly()) {
// tailor error message
if (shouldAttemptFixes()) {
auto *fix = MustBeCopyable::create(*this, type1,
getConstraintLocator(locator));
auto *fix = MustBeCopyable::create(*this,
type1,
NoncopyableMatchFailure::forExistentialCast(
type2),
getConstraintLocator(locator));
if (!recordFix(fix))
return getTypeMatchSuccess();
}
Expand Down Expand Up @@ -8495,7 +8498,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
// If this is a failure to conform to Copyable, tailor the error message.
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
auto *fix =
MustBeCopyable::create(*this, type, getConstraintLocator(locator));
MustBeCopyable::create(*this,
type,
NoncopyableMatchFailure::forCopyableConstraint(),
getConstraintLocator(locator));
if (!recordFix(fix))
return SolutionKind::Solved;
}
Expand Down
6 changes: 5 additions & 1 deletion lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
// Diagnose attempts to form a tuple with any noncopyable elements.
if (E->getType()->isPureMoveOnly()
&& !Ctx.LangOpts.hasFeature(Feature::MoveOnlyTuples)) {
Ctx.Diags.diagnose(E->getLoc(), diag::tuple_move_only_not_supported);
auto noncopyableTy = E->getType();
assert(noncopyableTy->is<TupleType>() && "will use poor wording");
Ctx.Diags.diagnose(E->getLoc(),
diag::tuple_containing_move_only_not_supported,
noncopyableTy);
}
}

Expand Down
Loading