Skip to content
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
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
27 changes: 27 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
21 changes: 19 additions & 2 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");
}
Expand Down
27 changes: 27 additions & 0 deletions test/Constraints/type_of.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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}}