diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e00d8a8dcff9a..55edd50aa711a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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", diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index dbfff2be4ea47..b7ee59d1ea460 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -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 @@ class IgnoreAssignmentDestinationType final : public ContextualMismatch { } }; +/// 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. diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index cfa974789ccd8..0196457639458 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -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 diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 51b0453841ddd..8d50477c0fa99 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -2428,6 +2428,15 @@ class AssignmentTypeMismatchFailure final : public ContextualFailure { 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; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index d0a27a56131f4..6c734b8937643 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -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(), diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 27675a08699d0..352dd8e82d79e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -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"); } diff --git a/test/Constraints/type_of.swift b/test/Constraints/type_of.swift index 57ea89c951ec7..226d415e0474b 100644 --- a/test/Constraints/type_of.swift +++ b/test/Constraints/type_of.swift @@ -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 {} + 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}}