Skip to content

[CS] Don't crash when default argument is magic literal and types don't match #26074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 17, 2019
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClosureExpr>(getAnchor());
auto params = closure->getParameters();
Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned, 1>;

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Param> synthesizedArgs,
Expand Down
24 changes: 24 additions & 0 deletions lib/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<unsigned, 1>;

Expand Down
38 changes: 36 additions & 2 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MagicIdentifierLiteralExpr>(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 &param = params[paramIdx];
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/ConstraintLocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -147,6 +149,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
case ApplyFunction:
case GenericParameter:
case FunctionArgument:
case DefaultArgument:
case FunctionResult:
case OptionalPayload:
case Member:
Expand Down Expand Up @@ -244,6 +247,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
return 0;

case FunctionArgument:
case DefaultArgument:
case FunctionResult:
return IsFunctionConversion;
}
Expand Down
9 changes: 9 additions & 0 deletions test/decl/func/default-values.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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'}}
5 changes: 5 additions & 0 deletions validation-test/compiler_crashers_2_fixed/sr11074.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: not %target-swift-frontend -typecheck %s

func foo(x: Int) {}
func foo(line: String = #line) {}
foo()