Skip to content

More correct implementation of SE-0110 #6133

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 3 commits into from
Dec 14, 2016
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: 0 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2621,9 +2621,6 @@ ERROR(tuple_pattern_in_non_tuple_context,none,
ERROR(closure_argument_list_tuple,none,
"contextual closure type %0 expects %1 argument%s1, "
"but %2 %select{were|was}3 used in closure body", (Type, unsigned, unsigned, bool))
ERROR(closure_argument_list_single_tuple,none,
"contextual closure type specifies %0, but %1 %select{was|were}2 used in closure body, "
"try adding extra parentheses around the single tuple argument", (Type, unsigned, bool))
ERROR(closure_argument_list_missing,none,
"contextual type for closure argument list expects %0 argument%s0, "
"which cannot be implicitly ignored", (unsigned))
Expand Down
9 changes: 9 additions & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4702,6 +4702,15 @@ Expr *ExprRewriter::coerceCallArguments(
bool hasTrailingClosure,
ConstraintLocatorBuilder locator) {

// Total hack: In Swift 3 mode, we can end up with an arity mismatch due to
// loss of ParenType sugar.
if (cs.getASTContext().isSwiftVersion3()) {
if (isa<TupleExpr>(arg))
if (auto *parenType = dyn_cast<ParenType>(paramType.getPointer()))
if (isa<TupleType>(parenType->getUnderlyingType().getPointer()))
paramType = parenType->getUnderlyingType();
}

bool allParamsMatch = cs.getType(arg)->isEqual(paramType);

// Find the callee declaration.
Expand Down
8 changes: 7 additions & 1 deletion lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5051,7 +5051,13 @@ static bool diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI,
TC.Context.SourceMgr, FnExpr->getEndLoc());
}
} else {
llvm_unreachable("unexpected argument expression type");
// FIXME: Due to a quirk of CSApply, we can end up without a
// ParenExpr if the argument has an '@lvalue TupleType'.
assert((isa<TupleType>(ArgExpr->getType().getPointer()) ||
isa<ParenType>(ArgExpr->getType().getPointer())) &&
"unexpected argument expression type");
insertLoc = ArgExpr->getLoc();

// Can't be TupleShuffleExpr because this argExpr is not yet resolved.
}

Expand Down
67 changes: 50 additions & 17 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1210,25 +1210,48 @@ ConstraintSystem::SolutionKind
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
// If we have type variables that have been bound to fixed types, look through
// to the fixed type.
bool isArgumentTupleConversion
= kind == ConstraintKind::ArgumentTupleConversion ||
kind == ConstraintKind::OperatorArgumentTupleConversion;
type1 = getFixedTypeRecursive(type1, flags, kind == ConstraintKind::Equal,
isArgumentTupleConversion);

auto desugar1 = type1->getDesugaredType();
TypeVariableType *typeVar1 = desugar1->getAs<TypeVariableType>();
// If we're doing an argument tuple conversion, or just matching the input
// types of two function types, we have to be careful to preserve
// ParenType sugar.
bool isArgumentTupleMatch = isArgumentTupleConversion;
bool isSwiftVersion3 = getASTContext().isSwiftVersion3();

// ... but not in Swift 3 mode, where this behavior was broken.
if (!isSwiftVersion3)
if (auto elt = locator.last())
if (elt->getKind() == ConstraintLocator::FunctionArgument)
isArgumentTupleMatch = true;

// If we have type variables that have been bound to fixed types, look through
// to the fixed type.
type1 = getFixedTypeRecursive(type1, flags, kind == ConstraintKind::Equal,
isArgumentTupleMatch);
type2 = getFixedTypeRecursive(type2, flags, kind == ConstraintKind::Equal,
isArgumentTupleConversion);
isArgumentTupleMatch);

auto desugar1 = type1->getDesugaredType();
auto desugar2 = type2->getDesugaredType();
TypeVariableType *typeVar2 = desugar2->getAs<TypeVariableType>();
TypeVariableType *typeVar1, *typeVar2;
if (isArgumentTupleMatch &&
!isSwiftVersion3) {
typeVar1 = dyn_cast<TypeVariableType>(type1.getPointer());
typeVar2 = dyn_cast<TypeVariableType>(type2.getPointer());

// If the types are obviously equivalent, we're done.
if (type1.getPointer() == type2.getPointer())
return SolutionKind::Solved;
} else {
typeVar1 = desugar1->getAs<TypeVariableType>();
typeVar2 = desugar2->getAs<TypeVariableType>();

// If the types are obviously equivalent, we're done.
if (desugar1->isEqual(desugar2))
return SolutionKind::Solved;
// If the types are obviously equivalent, we're done.
if (desugar1->isEqual(desugar2))
return SolutionKind::Solved;
}

// Local function that should be used to produce the return value whenever
// this function was unable to resolve the constraint. It should be used
Expand Down Expand Up @@ -1435,12 +1458,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
}
}

bool isTypeVarOrMember1 = desugar1->isTypeVariableOrMember();
bool isTypeVarOrMember2 = desugar2->isTypeVariableOrMember();

llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;
bool concrete = !isTypeVarOrMember1 && !isTypeVarOrMember2;

// If this is an argument conversion, handle it directly. The rules are
// different from normal conversions.
if (kind == ConstraintKind::ArgumentTupleConversion ||
Expand All @@ -1452,6 +1469,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
return formUnsolvedResult();
}

if (isArgumentTupleMatch &&
!isSwiftVersion3) {
if (!typeVar1 && !typeVar2) {
if (isa<ParenType>(type1.getPointer()) !=
isa<ParenType>(type2.getPointer())) {
return SolutionKind::Error;
}
}
}

bool isTypeVarOrMember1 = desugar1->isTypeVariableOrMember();
bool isTypeVarOrMember2 = desugar2->isTypeVariableOrMember();

llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;
bool concrete = !isTypeVarOrMember1 && !isTypeVarOrMember2;

// Decompose parallel structure.
TypeMatchOptions subflags =
getDefaultDecompositionOptions(flags) - TMF_ApplyingFix;
Expand Down
18 changes: 0 additions & 18 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1500,24 +1500,6 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
convertType.getType()->hasUnresolvedType())) {
convertType = TypeLoc();
convertTypePurpose = CTP_Unused;
} else if (auto closure = dyn_cast<ClosureExpr>(expr)) {
auto *P = closure->getParameters();

if (P->size() == 1 && convertType.getType()->is<FunctionType>()) {
auto hintFnType = convertType.getType()->castTo<FunctionType>();
auto hintFnInputType = hintFnType->getInput();

// Cannot use hintFnInputType->is<TupleType>() since it would desugar ParenType
if (isa<TupleType>(hintFnInputType.getPointer())) {
TupleType *tupleTy = hintFnInputType->castTo<TupleType>();

if (tupleTy->getNumElements() >= 2) {
diagnose(P->getStartLoc(), diag::closure_argument_list_single_tuple,
hintFnInputType, P->size(), P->size() > 1);
return true;
}
}
}
}
}

Expand Down
Loading