Skip to content

Commit 9883477

Browse files
authored
Merge pull request #84699 from hamishknight/meta-error
[CS] Improve diagnostics for non-metatype `type(of:)` contextual type
2 parents d3214de + d65f289 commit 9883477

File tree

7 files changed

+116
-2
lines changed

7 files changed

+116
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ ERROR(cannot_convert_return_type_to_anyobject,none,
359359
ERROR(cannot_convert_to_return_type_nil,none,
360360
"'nil' is incompatible with return type %0", (Type))
361361

362+
ERROR(cannot_convert_metatype_to_non_metatype,none,
363+
"cannot convert metatype %0 to non-metatype %1", (Type, Type))
364+
ERROR(cannot_convert_typeof_to_non_metatype,none,
365+
"cannot convert 'type(of:)' metatype to non-metatype %0", (Type))
366+
362367
ERROR(cannot_convert_thrown_type,none,
363368
"thrown expression type %0 %select{cannot be converted to error type %1|"
364369
"does not conform to 'Error'}2",

include/swift/Sema/CSFix.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,9 @@ enum class FixKind : uint8_t {
394394
/// Ignore a type imposed by an assignment destination e.g. `let x: Int = ...`
395395
IgnoreAssignmentDestinationType,
396396

397+
/// Ignore a non-metatype contextual type for a `type(of:)` expression.
398+
IgnoreNonMetatypeDynamicType,
399+
397400
/// Allow argument-to-parameter subtyping even when parameter type
398401
/// is marked as `inout`.
399402
AllowConversionThroughInOut,
@@ -2434,6 +2437,30 @@ class IgnoreAssignmentDestinationType final : public ContextualMismatch {
24342437
}
24352438
};
24362439

2440+
/// Ignore a non-metatype contextual type for a `type(of:)` expression, for
2441+
/// example `let x: Int = type(of: foo)`.
2442+
class IgnoreNonMetatypeDynamicType final : public ContextualMismatch {
2443+
IgnoreNonMetatypeDynamicType(ConstraintSystem &cs, Type instanceTy,
2444+
Type metatypeTy, ConstraintLocator *locator)
2445+
: ContextualMismatch(cs, FixKind::IgnoreNonMetatypeDynamicType,
2446+
instanceTy, metatypeTy, locator) {}
2447+
2448+
public:
2449+
std::string getName() const override {
2450+
return "ignore non-metatype result for 'type(of:)'";
2451+
}
2452+
2453+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2454+
2455+
static IgnoreNonMetatypeDynamicType *create(ConstraintSystem &cs,
2456+
Type instanceTy, Type metatypeTy,
2457+
ConstraintLocator *locator);
2458+
2459+
static bool classof(const ConstraintFix *fix) {
2460+
return fix->getKind() == FixKind::IgnoreNonMetatypeDynamicType;
2461+
}
2462+
};
2463+
24372464
/// If this is an argument-to-parameter conversion which is associated with
24382465
/// `inout` parameter, subtyping is not permitted, types have to
24392466
/// be identical.

lib/Sema/CSDiagnostics.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8189,6 +8189,20 @@ bool AssignmentTypeMismatchFailure::diagnoseAsNote() {
81898189
return false;
81908190
}
81918191

8192+
bool NonMetatypeDynamicTypeFailure::diagnoseAsError() {
8193+
auto instanceTy = getFromType();
8194+
auto metatypeTy = getToType();
8195+
if (instanceTy->isBareErrorType()) {
8196+
emitDiagnostic(diag::cannot_convert_typeof_to_non_metatype, metatypeTy)
8197+
.highlight(getSourceRange());
8198+
} else {
8199+
emitDiagnostic(diag::cannot_convert_metatype_to_non_metatype,
8200+
MetatypeType::get(instanceTy), metatypeTy)
8201+
.highlight(getSourceRange());
8202+
}
8203+
return true;
8204+
}
8205+
81928206
bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
81938207
auto *anchor = castToExpr(getAnchor());
81948208
// Member reference could be wrapped into a number of parens

lib/Sema/CSDiagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,15 @@ class AssignmentTypeMismatchFailure final : public ContextualFailure {
24282428
bool diagnoseMissingConformance() const;
24292429
};
24302430

2431+
class NonMetatypeDynamicTypeFailure final : public ContextualFailure {
2432+
public:
2433+
NonMetatypeDynamicTypeFailure(const Solution &solution, Type instanceTy,
2434+
Type metatypeTy, ConstraintLocator *locator)
2435+
: ContextualFailure(solution, instanceTy, metatypeTy, locator) {}
2436+
2437+
bool diagnoseAsError() override;
2438+
};
2439+
24312440
class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic {
24322441
DeclNameRef MemberName;
24332442

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,21 @@ IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy,
18251825
IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator);
18261826
}
18271827

1828+
IgnoreNonMetatypeDynamicType *
1829+
IgnoreNonMetatypeDynamicType::create(ConstraintSystem &cs, Type instanceTy,
1830+
Type metatypeTy,
1831+
ConstraintLocator *locator) {
1832+
return new (cs.getAllocator())
1833+
IgnoreNonMetatypeDynamicType(cs, instanceTy, metatypeTy, locator);
1834+
}
1835+
1836+
bool IgnoreNonMetatypeDynamicType::diagnose(const Solution &solution,
1837+
bool asNote) const {
1838+
NonMetatypeDynamicTypeFailure failure(solution, getFromType(), getToType(),
1839+
getLocator());
1840+
return failure.diagnose(asNote);
1841+
}
1842+
18281843
bool AllowInOutConversion::diagnose(const Solution &solution,
18291844
bool asNote) const {
18301845
InOutConversionFailure failure(solution, getFromType(), getToType(),

lib/Sema/CSSimplify.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12541,8 +12541,24 @@ ConstraintSystem::simplifyDynamicTypeOfConstraint(
1254112541
locator);
1254212542
}
1254312543

12544-
// It's definitely not either kind of metatype, so we can
12545-
// report failure right away.
12544+
// We don't have a non-metatype result, produce a fix.
12545+
if (shouldAttemptFixes()) {
12546+
// If we have a hole as a contextual type, eagerly produce holes in the
12547+
// argument of `type(of:)`.
12548+
if (type1->isPlaceholder()) {
12549+
recordTypeVariablesAsHoles(type2);
12550+
return SolutionKind::Solved;
12551+
}
12552+
12553+
// Otherwise we have some invalid contextual type, record a fix and let the
12554+
// argument be turned into a hole if needed.
12555+
recordAnyTypeVarAsPotentialHole(type2);
12556+
12557+
recordFix(IgnoreNonMetatypeDynamicType::create(
12558+
*this, type2, type1, getConstraintLocator(locator)));
12559+
return SolutionKind::Solved;
12560+
}
12561+
1254612562
return SolutionKind::Error;
1254712563
}
1254812564

@@ -16100,6 +16116,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1610016116
case FixKind::IgnoreMissingEachKeyword:
1610116117
case FixKind::AllowInlineArrayLiteralCountMismatch:
1610216118
case FixKind::TooManyDynamicMemberLookups:
16119+
case FixKind::IgnoreNonMetatypeDynamicType:
1610316120
case FixKind::IgnoreIsolatedConformance:
1610416121
llvm_unreachable("handled elsewhere");
1610516122
}

test/Constraints/type_of.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,30 @@ do {
8787
}
8888
}
8989
}
90+
91+
_ = { x in // expected-error {{cannot infer type of closure parameter 'x' without a type annotation}}
92+
let _: Undefined = Swift.type(of: x)
93+
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
94+
}
95+
_ = {
96+
func foo<T>() -> T {}
97+
let _: Undefined = Swift.type(of: foo())
98+
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
99+
}
100+
_ = {
101+
let _: Undefined = Swift.type(of: .foo)
102+
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
103+
}
104+
let _: Int = Swift.type(of: .foo)
105+
// expected-error@-1 {{cannot convert 'type(of:)' metatype to non-metatype 'Int'}}
106+
107+
let _ = Swift.type(of: .foo)
108+
// expected-error@-1 {{cannot infer contextual base in reference to member 'foo'}}
109+
110+
// FIXME: Ideally we'd include the type of the argument in the diagnostic, currently
111+
// we bind it to a hole before we open the closure.
112+
let _: Int = Swift.type(of: { (); return 0 }())
113+
// expected-error@-1 {{cannot convert 'type(of:)' metatype to non-metatype 'Int'}}
114+
115+
let _: Undefined = Swift.type(of: { (); return 0 }())
116+
// expected-error@-1 {{cannot find type 'Undefined' in scope}}

0 commit comments

Comments
 (0)