diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 56cb7648204d2..537255c2c33d7 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -350,6 +350,9 @@ ERROR(cannot_convert_default_arg_value,none, (Type,Type)) ERROR(cannot_convert_default_arg_value_protocol,none, "default argument value of type %0 does not conform to %1", (Type,Type)) +ERROR(default_argument_literal_cannot_convert, none, + "cannot call %0 %1 because default argument of type %2 cannot be " + "converted to type %3", (DescriptiveDeclKind, DeclName, Type, Type)) ERROR(cannot_convert_default_arg_value_nil,none, "nil default argument value cannot be converted to type %0", (Type)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ad9123ad2e622..53c6ace26b8ea 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2728,6 +2728,21 @@ bool MissingArgumentsFailure::diagnoseTrailingClosure(ClosureExpr *closure) { return true; } +bool DefaultArgumentTypeMismatch::diagnoseAsError() { + auto choice = getChoiceFor(getRawAnchor()); + if (!choice.hasValue()) + return false; + + auto declName = choice.getValue().choice.getName(); + auto declKind = choice.getValue().choice.getDecl()->getDescriptiveKind(); + + emitDiagnostic(getAnchor()->getLoc(), + diag::default_argument_literal_cannot_convert, declKind, + declName, FromType, ToType); + + return true; +} + bool ClosureParamDestructuringFailure::diagnoseAsError() { auto *closure = cast(getAnchor()); auto params = closure->getParameters(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index d510d33ca069c..d0898d87e6aa1 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1086,6 +1086,21 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool diagnoseTrailingClosure(ClosureExpr *closure); }; +class DefaultArgumentTypeMismatch final : public FailureDiagnostic { + using Param = AnyFunctionType::Param; + + Type FromType; + Type ToType; + +public: + DefaultArgumentTypeMismatch(Expr *root, ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) + : FailureDiagnostic(root, cs, locator), FromType(fromType), + ToType(toType) {} + + bool diagnoseAsError() override; +}; + class OutOfOrderArgumentFailure final : public FailureDiagnostic { using ParamBinding = SmallVector; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index db9208dce62f8..a6fed9d709334 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -451,6 +451,21 @@ bool AddMissingArguments::diagnose(Expr *root, bool asNote) const { return failure.diagnose(asNote); } +IgnoreDefaultArgumentTypeMismatch * +IgnoreDefaultArgumentTypeMismatch::create(ConstraintSystem &cs, Type fromType, + Type toType, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + IgnoreDefaultArgumentTypeMismatch(cs, fromType, toType, locator); +} + +bool IgnoreDefaultArgumentTypeMismatch::diagnose(Expr *root, + bool asNote) const { + DefaultArgumentTypeMismatch failure(root, getConstraintSystem(), FromType, + ToType, getLocator()); + return failure.diagnose(asNote); +} + AddMissingArguments * AddMissingArguments::create(ConstraintSystem &cs, FunctionType *funcType, llvm::ArrayRef synthesizedArgs, diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index f3c4ef8815703..6eb3f7d30469e 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -185,6 +185,9 @@ enum class FixKind : uint8_t { /// when base is an r-value type. AllowMutatingMemberOnRValueBase, + /// Fix the type of the default argument + DefaultArgumentTypeMismatch, + /// Allow a single tuple parameter to be matched with N arguments /// by forming all of the given arguments into a single tuple. AllowTupleSplatForSingleParameter, @@ -946,6 +949,27 @@ class AddMissingArguments final } }; +class IgnoreDefaultArgumentTypeMismatch final : public ConstraintFix { + Type FromType; + Type ToType; + + IgnoreDefaultArgumentTypeMismatch(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::DefaultArgumentTypeMismatch, locator), + FromType(fromType), ToType(toType) {} + +public: + std::string getName() const override { + return "ignore default argument type mismatch"; + } + + bool diagnose(Expr *root, bool asNote = false) const override; + + static IgnoreDefaultArgumentTypeMismatch *create(ConstraintSystem &cs, + Type fromType, Type toType, + ConstraintLocator *locator); +}; + class MoveOutOfOrderArgument final : public ConstraintFix { using ParamBinding = SmallVector; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d3b805eb3e0cc..55366e48ae32f 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -966,9 +966,36 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( for (unsigned paramIdx = 0, numParams = parameterBindings.size(); paramIdx != numParams; ++paramIdx){ - // Skip unfulfilled parameters. There's nothing to do for them. - if (parameterBindings[paramIdx].empty()) + // If a parameter is unfulfilled, validate the default argument if it is a + // magic literal expression. + if (parameterBindings[paramIdx].empty()) { + if (!callee) + continue; + + if (!callee->hasParameterList()) + continue; + + auto param = getParameterAt(callee, paramIdx); + + auto defaultValueExpr = param->getDefaultValue(); + + if (!(defaultValueExpr && + isa(defaultValueExpr))) + continue; + + if (defaultValueExpr->getType()) + continue; + + cs.generateConstraints(defaultValueExpr, param->getDeclContext()); + + auto defaultTy = cs.getType(defaultValueExpr); + auto paramTy = param->getType(); + cs.addConstraint( + ConstraintKind::ArgumentConversion, defaultTy, paramTy, + locator.withPathElement(ConstraintLocator::DefaultArgument)); + continue; + } // Determine the parameter type. const auto ¶m = params[paramIdx]; @@ -2335,6 +2362,12 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::DefaultArgument: { + conversionsOrFixes.push_back(IgnoreDefaultArgumentTypeMismatch::create( + *this, lhs, rhs, getConstraintLocator(anchor, path))); + break; + } + case ConstraintLocator::TypeParameterRequirement: case ConstraintLocator::ConditionalRequirement: { // If dependent members are present here it's because @@ -6965,6 +6998,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SkipSuperclassRequirement: case FixKind::ContextualMismatch: case FixKind::AddMissingArguments: + case FixKind::DefaultArgumentTypeMismatch: case FixKind::SkipUnhandledConstructInFunctionBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: { diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index c7f1460177cd1..df96f8248f593 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -52,6 +52,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case ApplyArgument: case ApplyFunction: case FunctionArgument: + case DefaultArgument: case FunctionResult: case OptionalPayload: case Member: @@ -270,6 +271,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { out << "function argument"; break; + case DefaultArgument: + out << "default argument"; + break; + case FunctionResult: out << "function result"; break; diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index f5e858de9b732..916261e584121 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -65,6 +65,8 @@ class ConstraintLocator : public llvm::FoldingSetNode { GenericParameter, /// The argument type of a function. FunctionArgument, + /// The default argument type of a function. + DefaultArgument, /// The result type of a function. FunctionResult, /// A tuple element referenced by position. @@ -147,6 +149,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case ApplyFunction: case GenericParameter: case FunctionArgument: + case DefaultArgument: case FunctionResult: case OptionalPayload: case Member: @@ -244,6 +247,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { return 0; case FunctionArgument: + case DefaultArgument: case FunctionResult: return IsFunctionConversion; } diff --git a/test/decl/func/default-values.swift b/test/decl/func/default-values.swift index d20c242174f51..430fc60fe4483 100644 --- a/test/decl/func/default-values.swift +++ b/test/decl/func/default-values.swift @@ -168,3 +168,12 @@ let fooThing5 = Foo(a: 0, d: 1, h: nil) // expected-error {{missing argument for // Here b = false and g = nil, but we're checking that f doesn't get a default value let fooThing6 = Foo(a: 0, d: 1, e: 2, h: nil) // expected-error {{missing argument for parameter 'f' in call}} // expected-note@-29 {{'init(a:b:d:e:f:g:h:)' declared here}} + +// SR-11074 + +func sr_11074(x: Int) {} +func sr_11074(line: String = #line) {} // expected-error {{default argument value of type 'Int' cannot be converted to type 'String'}} +sr_11074() // expected-error {{cannot call global function 'sr_11074(line:)' because default argument of type 'Int' cannot be converted to type 'String'}} + +class SR_11074_C { init(line: String = #line) {} } // expected-error {{default argument value of type 'Int' cannot be converted to type 'String'}} +let _ = SR_11074_C() // expected-error {{cannot call initializer 'init(line:)' because default argument of type 'Int' cannot be converted to type 'String'}} diff --git a/validation-test/compiler_crashers_2_fixed/sr11074.swift b/validation-test/compiler_crashers_2_fixed/sr11074.swift new file mode 100644 index 0000000000000..78c8889eb4276 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11074.swift @@ -0,0 +1,5 @@ +// RUN: not %target-swift-frontend -typecheck %s + +func foo(x: Int) {} +func foo(line: String = #line) {} +foo()