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,
|
||||
"'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,
|
||||
"thrown expression type %0 %select{cannot be converted to error type %1|"
|
||||
"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 = ...`
|
||||
IgnoreAssignmentDestinationType,
|
||||
|
||||
/// Ignore a non-metatype contextual type for a `type(of:)` expression.
|
||||
IgnoreNonMetatypeDynamicType,
|
||||
|
||||
/// Allow argument-to-parameter subtyping even when parameter type
|
||||
/// is marked as `inout`.
|
||||
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
|
||||
/// `inout` parameter, subtyping is not permitted, types have to
|
||||
/// be identical.
|
||||
|
||||
@@ -8189,6 +8189,20 @@ bool AssignmentTypeMismatchFailure::diagnoseAsNote() {
|
||||
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() {
|
||||
auto *anchor = castToExpr(getAnchor());
|
||||
// Member reference could be wrapped into a number of parens
|
||||
|
||||
@@ -2428,6 +2428,15 @@ private:
|
||||
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 {
|
||||
DeclNameRef MemberName;
|
||||
|
||||
|
||||
@@ -1825,6 +1825,21 @@ IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy,
|
||||
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 asNote) const {
|
||||
InOutConversionFailure failure(solution, getFromType(), getToType(),
|
||||
|
||||
@@ -12541,8 +12541,24 @@ ConstraintSystem::simplifyDynamicTypeOfConstraint(
|
||||
locator);
|
||||
}
|
||||
|
||||
// It's definitely not either kind of metatype, so we can
|
||||
// report failure right away.
|
||||
// We don't have a non-metatype result, produce a fix.
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -16100,6 +16116,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
||||
case FixKind::IgnoreMissingEachKeyword:
|
||||
case FixKind::AllowInlineArrayLiteralCountMismatch:
|
||||
case FixKind::TooManyDynamicMemberLookups:
|
||||
case FixKind::IgnoreNonMetatypeDynamicType:
|
||||
case FixKind::IgnoreIsolatedConformance:
|
||||
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