mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[CS] Improve diagnostics for non-metatype type(of:) contextual type
Emit a custom diagnostic for this case, and handle holes.
This commit is contained in:
@@ -359,6 +359,11 @@ ERROR(cannot_convert_return_type_to_anyobject,none,
|
|||||||
ERROR(cannot_convert_to_return_type_nil,none,
|
ERROR(cannot_convert_to_return_type_nil,none,
|
||||||
"'nil' is incompatible with return type %0", (Type))
|
"'nil' is incompatible with return type %0", (Type))
|
||||||
|
|
||||||
|
ERROR(cannot_convert_metatype_to_non_metatype,none,
|
||||||
|
"cannot convert metatype %0 to non-metatype %1", (Type, Type))
|
||||||
|
ERROR(cannot_convert_typeof_to_non_metatype,none,
|
||||||
|
"cannot convert 'type(of:)' metatype to non-metatype %0", (Type))
|
||||||
|
|
||||||
ERROR(cannot_convert_thrown_type,none,
|
ERROR(cannot_convert_thrown_type,none,
|
||||||
"thrown expression type %0 %select{cannot be converted to error type %1|"
|
"thrown expression type %0 %select{cannot be converted to error type %1|"
|
||||||
"does not conform to 'Error'}2",
|
"does not conform to 'Error'}2",
|
||||||
|
|||||||
@@ -394,6 +394,9 @@ enum class FixKind : uint8_t {
|
|||||||
/// Ignore a type imposed by an assignment destination e.g. `let x: Int = ...`
|
/// Ignore a type imposed by an assignment destination e.g. `let x: Int = ...`
|
||||||
IgnoreAssignmentDestinationType,
|
IgnoreAssignmentDestinationType,
|
||||||
|
|
||||||
|
/// Ignore a non-metatype contextual type for a `type(of:)` expression.
|
||||||
|
IgnoreNonMetatypeDynamicType,
|
||||||
|
|
||||||
/// Allow argument-to-parameter subtyping even when parameter type
|
/// Allow argument-to-parameter subtyping even when parameter type
|
||||||
/// is marked as `inout`.
|
/// is marked as `inout`.
|
||||||
AllowConversionThroughInOut,
|
AllowConversionThroughInOut,
|
||||||
@@ -2434,6 +2437,30 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Ignore a non-metatype contextual type for a `type(of:)` expression, for
|
||||||
|
/// example `let x: Int = type(of: foo)`.
|
||||||
|
class IgnoreNonMetatypeDynamicType final : public ContextualMismatch {
|
||||||
|
IgnoreNonMetatypeDynamicType(ConstraintSystem &cs, Type instanceTy,
|
||||||
|
Type metatypeTy, ConstraintLocator *locator)
|
||||||
|
: ContextualMismatch(cs, FixKind::IgnoreNonMetatypeDynamicType,
|
||||||
|
instanceTy, metatypeTy, locator) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string getName() const override {
|
||||||
|
return "ignore non-metatype result for 'type(of:)'";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool diagnose(const Solution &solution, bool asNote = false) const override;
|
||||||
|
|
||||||
|
static IgnoreNonMetatypeDynamicType *create(ConstraintSystem &cs,
|
||||||
|
Type instanceTy, Type metatypeTy,
|
||||||
|
ConstraintLocator *locator);
|
||||||
|
|
||||||
|
static bool classof(const ConstraintFix *fix) {
|
||||||
|
return fix->getKind() == FixKind::IgnoreNonMetatypeDynamicType;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// If this is an argument-to-parameter conversion which is associated with
|
/// If this is an argument-to-parameter conversion which is associated with
|
||||||
/// `inout` parameter, subtyping is not permitted, types have to
|
/// `inout` parameter, subtyping is not permitted, types have to
|
||||||
/// be identical.
|
/// be identical.
|
||||||
|
|||||||
@@ -8189,6 +8189,20 @@ bool AssignmentTypeMismatchFailure::diagnoseAsNote() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NonMetatypeDynamicTypeFailure::diagnoseAsError() {
|
||||||
|
auto instanceTy = getFromType();
|
||||||
|
auto metatypeTy = getToType();
|
||||||
|
if (instanceTy->isBareErrorType()) {
|
||||||
|
emitDiagnostic(diag::cannot_convert_typeof_to_non_metatype, metatypeTy)
|
||||||
|
.highlight(getSourceRange());
|
||||||
|
} else {
|
||||||
|
emitDiagnostic(diag::cannot_convert_metatype_to_non_metatype,
|
||||||
|
MetatypeType::get(instanceTy), metatypeTy)
|
||||||
|
.highlight(getSourceRange());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
|
bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
|
||||||
auto *anchor = castToExpr(getAnchor());
|
auto *anchor = castToExpr(getAnchor());
|
||||||
// Member reference could be wrapped into a number of parens
|
// Member reference could be wrapped into a number of parens
|
||||||
|
|||||||
@@ -2428,6 +2428,15 @@ private:
|
|||||||
bool diagnoseMissingConformance() const;
|
bool diagnoseMissingConformance() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NonMetatypeDynamicTypeFailure final : public ContextualFailure {
|
||||||
|
public:
|
||||||
|
NonMetatypeDynamicTypeFailure(const Solution &solution, Type instanceTy,
|
||||||
|
Type metatypeTy, ConstraintLocator *locator)
|
||||||
|
: ContextualFailure(solution, instanceTy, metatypeTy, locator) {}
|
||||||
|
|
||||||
|
bool diagnoseAsError() override;
|
||||||
|
};
|
||||||
|
|
||||||
class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic {
|
class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic {
|
||||||
DeclNameRef MemberName;
|
DeclNameRef MemberName;
|
||||||
|
|
||||||
|
|||||||
@@ -1825,6 +1825,21 @@ IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy,
|
|||||||
IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator);
|
IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IgnoreNonMetatypeDynamicType *
|
||||||
|
IgnoreNonMetatypeDynamicType::create(ConstraintSystem &cs, Type instanceTy,
|
||||||
|
Type metatypeTy,
|
||||||
|
ConstraintLocator *locator) {
|
||||||
|
return new (cs.getAllocator())
|
||||||
|
IgnoreNonMetatypeDynamicType(cs, instanceTy, metatypeTy, locator);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IgnoreNonMetatypeDynamicType::diagnose(const Solution &solution,
|
||||||
|
bool asNote) const {
|
||||||
|
NonMetatypeDynamicTypeFailure failure(solution, getFromType(), getToType(),
|
||||||
|
getLocator());
|
||||||
|
return failure.diagnose(asNote);
|
||||||
|
}
|
||||||
|
|
||||||
bool AllowInOutConversion::diagnose(const Solution &solution,
|
bool AllowInOutConversion::diagnose(const Solution &solution,
|
||||||
bool asNote) const {
|
bool asNote) const {
|
||||||
InOutConversionFailure failure(solution, getFromType(), getToType(),
|
InOutConversionFailure failure(solution, getFromType(), getToType(),
|
||||||
|
|||||||
@@ -12541,8 +12541,24 @@ ConstraintSystem::simplifyDynamicTypeOfConstraint(
|
|||||||
locator);
|
locator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's definitely not either kind of metatype, so we can
|
// We don't have a non-metatype result, produce a fix.
|
||||||
// report failure right away.
|
if (shouldAttemptFixes()) {
|
||||||
|
// If we have a hole as a contextual type, eagerly produce holes in the
|
||||||
|
// argument of `type(of:)`.
|
||||||
|
if (type1->isPlaceholder()) {
|
||||||
|
recordTypeVariablesAsHoles(type2);
|
||||||
|
return SolutionKind::Solved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we have some invalid contextual type, record a fix and let the
|
||||||
|
// argument be turned into a hole if needed.
|
||||||
|
recordAnyTypeVarAsPotentialHole(type2);
|
||||||
|
|
||||||
|
recordFix(IgnoreNonMetatypeDynamicType::create(
|
||||||
|
*this, type2, type1, getConstraintLocator(locator)));
|
||||||
|
return SolutionKind::Solved;
|
||||||
|
}
|
||||||
|
|
||||||
return SolutionKind::Error;
|
return SolutionKind::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16100,6 +16116,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||||||
case FixKind::IgnoreMissingEachKeyword:
|
case FixKind::IgnoreMissingEachKeyword:
|
||||||
case FixKind::AllowInlineArrayLiteralCountMismatch:
|
case FixKind::AllowInlineArrayLiteralCountMismatch:
|
||||||
case FixKind::TooManyDynamicMemberLookups:
|
case FixKind::TooManyDynamicMemberLookups:
|
||||||
|
case FixKind::IgnoreNonMetatypeDynamicType:
|
||||||
case FixKind::IgnoreIsolatedConformance:
|
case FixKind::IgnoreIsolatedConformance:
|
||||||
llvm_unreachable("handled elsewhere");
|
llvm_unreachable("handled elsewhere");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,3 +87,30 @@ do {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = { x in // expected-error {{cannot infer type of closure parameter 'x' without a type annotation}}
|
||||||
|
let _: Undefined = Swift.type(of: x)
|
||||||
|
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
|
||||||
|
}
|
||||||
|
_ = {
|
||||||
|
func foo<T>() -> T {}
|
||||||
|
let _: Undefined = Swift.type(of: foo())
|
||||||
|
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
|
||||||
|
}
|
||||||
|
_ = {
|
||||||
|
let _: Undefined = Swift.type(of: .foo)
|
||||||
|
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
|
||||||
|
}
|
||||||
|
let _: Int = Swift.type(of: .foo)
|
||||||
|
// expected-error@-1 {{cannot convert 'type(of:)' metatype to non-metatype 'Int'}}
|
||||||
|
|
||||||
|
let _ = Swift.type(of: .foo)
|
||||||
|
// expected-error@-1 {{cannot infer contextual base in reference to member 'foo'}}
|
||||||
|
|
||||||
|
// FIXME: Ideally we'd include the type of the argument in the diagnostic, currently
|
||||||
|
// we bind it to a hole before we open the closure.
|
||||||
|
let _: Int = Swift.type(of: { (); return 0 }())
|
||||||
|
// expected-error@-1 {{cannot convert 'type(of:)' metatype to non-metatype 'Int'}}
|
||||||
|
|
||||||
|
let _: Undefined = Swift.type(of: { (); return 0 }())
|
||||||
|
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
|
||||||
|
|||||||
Reference in New Issue
Block a user