Skip to content

Infer existential element/key/value types for collection literals as a fallback #4061

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
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
6 changes: 5 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2097,7 +2097,11 @@ ERROR(discard_expr_outside_of_assignment,none,
())
ERROR(inout_expr_outside_of_call,none,
"'&' can only appear immediately in a call argument list", ())

ERROR(collection_literal_heterogenous,none,
"heterogenous collection literal could only be inferred to %0; add"
" explicit type annotation if this is intentional", (Type))
ERROR(collection_literal_empty,none,
"empty collection literal requires an explicit type", ())

ERROR(unresolved_member_no_inference,none,
"reference to member %0 cannot be resolved without a contextual type",
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,10 @@ class CollectionExpr : public Expr {

Expr *SemanticExpr = nullptr;

/// True if the type of this collection expr was inferred by the collection
/// fallback type, like [Any].
bool IsTypeDefaulted = false;

protected:
CollectionExpr(ExprKind Kind, SourceLoc LBracketLoc,
MutableArrayRef<Expr*> Elements,
Expand All @@ -2157,6 +2161,9 @@ class CollectionExpr : public Expr {
void setElement(unsigned i, Expr *E) { Elements[i] = E; }
unsigned getNumElements() const { return Elements.size(); }

bool isTypeDefaulted() const { return IsTypeDefaulted; }
void setIsTypeDefaulted(bool value = true) { IsTypeDefaulted = value; }

SourceLoc getLBracketLoc() const { return LBracketLoc; }
SourceLoc getRBracketLoc() const { return RBracketLoc; }
SourceRange getSourceRange() const {
Expand Down
26 changes: 25 additions & 1 deletion include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,30 @@ class Type {

/// Get the canonical type, or return null if the type is null.
CanType getCanonicalTypeOrNull() const; // in Types.h


/// Computes the meet between two types.
///
/// The meet of two types is the most specific type that is a supertype of
/// both \c type1 and \c type2. For example, given a simple class hierarchy as
/// follows:
///
/// \code
/// class A { }
/// class B : A { }
/// class C : A { }
/// class D { }
/// \endcode
///
/// The meet of B and C is A, the meet of A and B is A. However, there is no
/// meet of D and A (or D and B, or D and C) because there is no common
/// superclass. One would have to jump to an existential (e.g., \c AnyObject)
/// to find a common type.
///
/// \returns the meet of the two types, if there is a concrete type that can
/// express the meet, or a null type if the only meet would be a more-general
/// existential type (e.g., \c Any).
static Type meet(Type type1, Type type2);

private:
// Direct comparison is disabled for types, because they may not be canonical.
void operator==(Type T) const = delete;
Expand Down Expand Up @@ -432,6 +455,7 @@ class CanGenericSignature {
return Signature;
}
};

} // end namespace swift

namespace llvm {
Expand Down
1 change: 1 addition & 0 deletions lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ add_swift_library(swiftAST STATIC
SourceEntityWalker.cpp
Substitution.cpp
Type.cpp
TypeJoinMeet.cpp
TypeRefinementContext.cpp
TypeRepr.cpp
TypeWalker.cpp
Expand Down
70 changes: 70 additions & 0 deletions lib/AST/TypeJoinMeet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===--- TypeJoinMeet.cpp - Swift Type "Join" and "Meet" -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements the "meet" operation for types (and, eventually,
// "join").
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTContext.h"
#include "swift/AST/Type.h"
#include "swift/AST/Types.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace swift;

Type Type::meet(Type type1, Type type2) {
assert(!type1->hasTypeVariable() && !type2->hasTypeVariable() &&
"Cannot compute meet of types involving type variables");

// FIXME: This algorithm is woefully incomplete, and is only currently used
// for optimizing away extra exploratory work in the constraint solver. It
// should eventually encompass all of the subtyping rules of the language.

// If the types are equivalent, the meet is obvious.
if (type1->isEqual(type2))
return type1;

// If both are class types or opaque types that potentially have superclasses,
// find the common superclass.
if (type1->mayHaveSuperclass() && type2->mayHaveSuperclass()) {
ASTContext &ctx = type1->getASTContext();
LazyResolver *resolver = ctx.getLazyResolver();

/// Walk the superclasses of type1 looking for type2. Record them for our
/// second step.
llvm::SmallPtrSet<CanType, 8> superclassesOfType1;
CanType canType2 = type2->getCanonicalType();
for (Type super1 = type1; super1; super1 = super1->getSuperclass(resolver)){
CanType canSuper1 = super1->getCanonicalType();

// If we have found the second type, we're done.
if (canSuper1 == canType2) return super1;

superclassesOfType1.insert(canSuper1);
}

// Look through the superclasses of type2 to determine if any were also
// superclasses of type1.
for (Type super2 = type2; super2; super2 = super2->getSuperclass(resolver)){
CanType canSuper2 = super2->getCanonicalType();

// If we found the first type, we're done.
if (superclassesOfType1.count(canSuper2)) return super2;
}

// There is no common superclass; we're done.
return nullptr;
}

// The meet can only be an existential.
return nullptr;
}

20 changes: 20 additions & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2711,6 +2711,14 @@ namespace {

expr->setSemanticExpr(result);
expr->setType(arrayTy);

// If the array element type was defaulted, note that in the expression.
auto elementTypeVariable = cs.ArrayElementTypeVariables.find(expr);
if (elementTypeVariable != cs.ArrayElementTypeVariables.end()) {
if (solution.DefaultedTypeVariables.count(elementTypeVariable->second))
expr->setIsTypeDefaulted();
}

return expr;
}

Expand Down Expand Up @@ -2780,6 +2788,18 @@ namespace {

expr->setSemanticExpr(result);
expr->setType(dictionaryTy);

// If the dictionary key or value type was defaulted, note that in the
// expression.
auto elementTypeVariable = cs.DictionaryElementTypeVariables.find(expr);
if (elementTypeVariable != cs.DictionaryElementTypeVariables.end()) {
if (solution.DefaultedTypeVariables.count(
elementTypeVariable->second.first) ||
solution.DefaultedTypeVariables.count(
elementTypeVariable->second.second))
expr->setIsTypeDefaulted();
}

return expr;
}

Expand Down
42 changes: 37 additions & 5 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1728,12 +1728,18 @@ namespace {
LocatorPathElt::getTupleElement(index++)));
}

// The array element type defaults to 'Any'.
if (auto elementTypeVar = arrayElementTy->getAs<TypeVariableType>()) {
CS.addConstraint(ConstraintKind::Defaultable, arrayElementTy,
tc.Context.TheAnyType, locator);
CS.ArrayElementTypeVariables[expr] = elementTypeVar;
}

return arrayTy;
}

static bool isMergeableValueKind(Expr *expr) {
return isa<CollectionExpr>(expr) ||
isa<StringLiteralExpr>(expr) || isa<IntegerLiteralExpr>(expr) ||
return isa<StringLiteralExpr>(expr) || isa<IntegerLiteralExpr>(expr) ||
isa<FloatLiteralExpr>(expr);
}

Expand Down Expand Up @@ -1811,16 +1817,22 @@ namespace {
auto keyTyvar2 = tty2->getElementTypes()[0]->
getAs<TypeVariableType>();

mergedKey = mergeRepresentativeEquivalenceClasses(CS,
auto keyExpr1 = cast<TupleExpr>(element1)->getElements()[0];
auto keyExpr2 = cast<TupleExpr>(element2)->getElements()[0];

if (keyExpr1->getKind() == keyExpr2->getKind() &&
isMergeableValueKind(keyExpr1)) {
mergedKey = mergeRepresentativeEquivalenceClasses(CS,
keyTyvar1, keyTyvar2);
}

auto valueTyvar1 = tty1->getElementTypes()[1]->
getAs<TypeVariableType>();
auto valueTyvar2 = tty2->getElementTypes()[1]->
getAs<TypeVariableType>();

auto elemExpr1 = dyn_cast<TupleExpr>(element1)->getElements()[1];
auto elemExpr2 = dyn_cast<TupleExpr>(element2)->getElements()[1];
auto elemExpr1 = cast<TupleExpr>(element1)->getElements()[1];
auto elemExpr2 = cast<TupleExpr>(element2)->getElements()[1];

if (elemExpr1->getKind() == elemExpr2->getKind() &&
isMergeableValueKind(elemExpr1)) {
Expand Down Expand Up @@ -1849,6 +1861,26 @@ namespace {
LocatorPathElt::getTupleElement(index++)));
}

// The dictionary key type defaults to 'AnyHashable'.
auto keyTypeVar = dictionaryKeyTy->getAs<TypeVariableType>();
if (keyTypeVar && tc.Context.getAnyHashableDecl()) {
auto anyHashable = tc.Context.getAnyHashableDecl();
tc.validateDecl(anyHashable);
CS.addConstraint(ConstraintKind::Defaultable, dictionaryKeyTy,
anyHashable->getDeclaredInterfaceType(), locator);
}

// The dictionary value type defaults to 'Any'.
auto valueTypeVar = dictionaryValueTy->getAs<TypeVariableType>();
if (valueTypeVar) {
CS.addConstraint(ConstraintKind::Defaultable, dictionaryValueTy,
tc.Context.TheAnyType, locator);
}

// Record key/value type variables.
if (keyTypeVar || valueTypeVar)
CS.DictionaryElementTypeVariables[expr] = { keyTypeVar, valueTypeVar };

return dictionaryTy;
}

Expand Down
Loading