[SE-0111] Drop argument labels on references to function values.

When referencing a function in the type checker, drop argument labels
when we don't need them to type-check an immediate call to that
function. This provides the semantic behavior of SE-0111, e.g.,
references to functions as values produce unlabeled function types,
without the representational change of actually dropping argument
labels from the type system.

At the moment, this only works for bare references to functions. It
still needs to be pushed through more of the type checker and more AST
nodes to work in the general case.

Keep this work behind the frontend flag
-suppress-argument-labels-in-types for now.
This commit is contained in:
Doug Gregor
2016-07-28 11:10:53 -07:00
parent 8c7e75afa0
commit a9536906ff
23 changed files with 539 additions and 124 deletions

View File

@@ -615,13 +615,41 @@ Type ConstraintSystem::openType(
return startingType.transform(replaceDependentTypes);
}
/// Remove argument labels from the function type.
static Type removeArgumentLabels(Type type, unsigned numArgumentLabels) {
// If there is nothing to remove, don't.
if (numArgumentLabels == 0) return type;
auto fnType = type->getAs<FunctionType>();
// Drop argument labels from the input type.
Type inputType = fnType->getInput();
if (auto tupleTy = dyn_cast<TupleType>(inputType.getPointer())) {
SmallVector<TupleTypeElt, 4> elements;
elements.reserve(tupleTy->getNumElements());
for (const auto &elt : tupleTy->getElements()) {
elements.push_back(TupleTypeElt(elt.getType(), Identifier(),
elt.isVararg()));
}
inputType = TupleType::get(elements, type->getASTContext());
}
return FunctionType::get(inputType,
removeArgumentLabels(fnType->getResult(),
numArgumentLabels - 1),
fnType->getExtInfo());
}
Type ConstraintSystem::openFunctionType(
AnyFunctionType *funcType,
unsigned numArgumentLabelsToRemove,
ConstraintLocatorBuilder locator,
llvm::DenseMap<CanType, TypeVariableType *> &replacements,
DeclContext *innerDC,
DeclContext *outerDC,
bool skipProtocolSelfConstraint) {
Type type;
if (auto *genericFn = funcType->getAs<GenericFunctionType>()) {
// Open up the generic parameters and requirements.
openGeneric(innerDC,
@@ -641,12 +669,15 @@ Type ConstraintSystem::openFunctionType(
return Type();
// Build the resulting (non-generic) function type.
return FunctionType::get(inputTy, resultTy,
type = FunctionType::get(inputTy, resultTy,
FunctionType::ExtInfo().
withThrows(genericFn->throws()));
withThrows(genericFn->throws()));
} else {
type = openType(funcType, locator, replacements);
if (!type) return Type();
}
return openType(funcType, locator, replacements);
return removeArgumentLabels(type, numArgumentLabelsToRemove);
}
bool ConstraintSystem::isArrayType(Type t) {
@@ -778,10 +809,47 @@ void ConstraintSystem::recordOpenedTypes(
replacements.size()) });
}
/// Determine how many levels of argument labels should be removed from the
/// function type when referencing the given declaration.
static unsigned getNumRemovedArgumentLabels(ASTContext &ctx, ValueDecl *decl,
bool isCurriedInstanceReference,
FunctionRefKind functionRefKind) {
// Is this functionality enabled at all?
if (!ctx.LangOpts.SuppressArgumentLabelsInTypes) return 0;
// Only applicable to functions. Nothing else should have argument labels in
// the type.
auto func = dyn_cast<AbstractFunctionDecl>(decl);
if (!func) return 0;
switch (functionRefKind) {
case FunctionRefKind::Unapplied:
case FunctionRefKind::Compound:
// Always remove argument labels from unapplied references and references
// that use a compound name.
return func->getNumParameterLists();
case FunctionRefKind::SingleApply:
// If we have fewer than two parameter lists, leave the labels.
if (func->getNumParameterLists() < 2) return 0;
// If this is a curried reference to an instance method, where 'self' is
// being applied, e.g., "ClassName.instanceMethod(self)", remove the
// argument labels from the resulting function type. The 'self' parameter is
// always unlabled, so this operation is a no-op for the actual application.
return isCurriedInstanceReference ? func->getNumParameterLists() : 1;
case FunctionRefKind::DoubleApply:
// Never remove argument labels from a double application.
return 0;
}
}
std::pair<Type, Type>
ConstraintSystem::getTypeOfReference(ValueDecl *value,
bool isTypeReference,
bool isSpecialized,
FunctionRefKind functionRefKind,
ConstraintLocatorBuilder locator,
const DeclRefExpr *base) {
llvm::DenseMap<CanType, TypeVariableType *> replacements;
@@ -793,6 +861,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
auto openedType = openFunctionType(
func->getInterfaceType()->castTo<AnyFunctionType>(),
/*numRemovedArgumentLabels=*/0,
locator, replacements,
func->getInnermostDeclContext(),
func->getDeclContext(),
@@ -862,7 +931,11 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
if (auto funcType = valueType->getAs<AnyFunctionType>()) {
valueType =
openFunctionType(
funcType, locator, replacements,
funcType,
getNumRemovedArgumentLabels(TC.Context, value,
/*isCurriedInstanceReference=*/false,
functionRefKind),
locator, replacements,
value->getInnermostDeclContext(),
value->getDeclContext(),
/*skipProtocolSelfConstraint=*/false);
@@ -1100,12 +1173,15 @@ static bool isRequirementOrWitness(const ConstraintLocatorBuilder &locator) {
std::pair<Type, Type>
ConstraintSystem::getTypeOfMemberReference(
Type baseTy, ValueDecl *value,
bool isTypeReference,
bool isDynamicResult,
ConstraintLocatorBuilder locator,
const DeclRefExpr *base,
llvm::DenseMap<CanType, TypeVariableType *> *replacementsPtr) {
Type baseTy, ValueDecl *value,
bool isTypeReference,
bool isDynamicResult,
ConstraintLocatorBuilder locator,
const DeclRefExpr *base,
llvm::DenseMap<CanType, TypeVariableType *> *replacementsPtr) {
// FIXME: Should receive the function reference kind as a parameter.
FunctionRefKind functionRefKind = FunctionRefKind::DoubleApply;
// Figure out the instance type used for the base.
TypeVariableType *baseTypeVar = nullptr;
Type baseObjTy = getFixedTypeRecursive(baseTy, baseTypeVar,
@@ -1119,7 +1195,7 @@ ConstraintSystem::getTypeOfMemberReference(
// If the base is a module type, just use the type of the decl.
if (baseObjTy->is<ModuleType>()) {
return getTypeOfReference(value, isTypeReference, /*isSpecialized=*/false,
locator, base);
functionRefKind, locator, base);
}
// Handle associated type lookup as a special case, horribly.
@@ -1178,8 +1254,12 @@ ConstraintSystem::getTypeOfMemberReference(
llvm::DenseMap<CanType, TypeVariableType *> localReplacements;
auto &replacements = replacementsPtr ? *replacementsPtr : localReplacements;
if (auto genericFn = value->getInterfaceType()->getAs<GenericFunctionType>()){
openedType = openFunctionType(genericFn, locator, replacements,
innerDC, outerDC,
bool isCurriedInstanceReference = value->isInstanceMember() && !isInstance;
unsigned numRemovedArgumentLabels =
getNumRemovedArgumentLabels(TC.Context, value, isCurriedInstanceReference,
functionRefKind);
openedType = openFunctionType(genericFn, numRemovedArgumentLabels,
locator, replacements, innerDC, outerDC,
/*skipProtocolSelfConstraint=*/true);
} else {
openedType = TC.getUnopenedTypeOfReference(value, baseTy, DC, base,
@@ -1433,7 +1513,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
} else {
std::tie(openedFullType, refType)
= getTypeOfReference(choice.getDecl(), isTypeReference,
choice.isSpecialized(), locator);
choice.isSpecialized(),
choice.getFunctionRefKind(), locator);
}
if (!isRequirementOrWitness(locator) &&