Skip to content

Commit

Permalink
Reapply r31105, with some fixes to invalid unconstrained generics. Th…
Browse files Browse the repository at this point in the history
…ese fixes correct

the regressions that r31105 introduced in the validation tests, as well as fixing a number
of other validation tests as well.

Introduce a new UnresolvedType to the type system, and have CSDiags start to use it
as a way to get more type information out of incorrect subexpressions.  UnresolvedType
generally just propagates around the type system like a type variable:
 - it magically conforms to all protocols
 - it CSGens as an unconstrained type variable.
 - it ASTPrints as _, just like a type variable.

The major difference is that UnresolvedType can be used outside the context of a
ConstraintSystem, which is useful for CSGen since it sets up several of them to 
diagnose subexpressions w.r.t. their types.

For now, our use of this is extremely limited: when a closureexpr has no contextual
type available and its parameters are invalid, we wipe them out with UnresolvedType
(instead of the previous nulltype dance) to get ambiguities later on.

We also introduce a new FreeTypeVariableBinding::UnresolvedType approach for
constraint solving (and use this only in one place in CSDiags so far, to resolve
the callee of a CallExpr) which solves a system and rewrites any leftover type 
variables as UnresolvedTypes.  This allows us to get more precise information out,
for example, diagnosing:

 func r22162441(lines: [String]) {
   lines.map { line in line.fooBar() }
 }

with: value of type 'String' has no member 'fooBar'
instead of: type of expression is ambiguous without more context

This improves a number of other diagnostics as well, but is just the infrastructural
stepping stone for greater things.





Swift SVN r31130
  • Loading branch information
lattner committed Aug 11, 2015
1 parent 66c8598 commit a899872
Show file tree
Hide file tree
Showing 55 changed files with 219 additions and 102 deletions.
1 change: 1 addition & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ class ASTContext {

// Builtin type and simple types that are used frequently.
const CanType TheErrorType; /// This is the ErrorType singleton.
const CanType TheUnresolvedType; /// This is the UnresolvedType singleton.
const CanType TheEmptyTupleType; /// This is "()", aka Void
const CanType TheNativeObjectType; /// Builtin.NativeObject
const CanType TheBridgeObjectType; /// Builtin.BridgeObject
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
#endif

UNCHECKED_TYPE(Error, Type)
UNCHECKED_TYPE(Unresolved, Type)
ABSTRACT_TYPE(Builtin, Type)
BUILTIN_TYPE(BuiltinInteger, BuiltinType)
BUILTIN_TYPE(BuiltinFloat, BuiltinType)
Expand Down
43 changes: 38 additions & 5 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,26 @@ class RecursiveTypeProperties {
/// This type expression contains a GenericTypeParamType.
HasTypeParameter = 0x04,

/// This type expression contains an UnresolvedType.
HasUnresolvedType = 0x08,


/// This type expression contains an LValueType or InOutType,
/// other than as a function input.
IsNotMaterializable = 0x08,
IsNotMaterializable = 0x10,

/// This type expression contains an LValueType and can be loaded to convert
/// to an rvalue.
IsLValue = 0x10,
IsLValue = 0x20,

/// This type expression contains an opened existential ArchetypeType.
HasOpenedExistential = 0x20,
HasOpenedExistential = 0x40,

/// This type expression contains a DynamicSelf type.
HasDynamicSelf = 0x40,
HasDynamicSelf = 0x80,

/// Whether this type expression contains an unbound generic type.
HasUnboundGeneric = 0x80,
HasUnboundGeneric = 0x100,
};

private:
Expand All @@ -132,6 +136,9 @@ class RecursiveTypeProperties {
/// Does a type with these properties have a type parameter somewhere in it?
bool hasTypeParameter() const { return Bits & HasTypeParameter; }

/// Does a type with these properties have an unresolved type somewhere in it?
bool hasUnresolvedType() const { return Bits & HasUnresolvedType; }

/// Is a type with these properties materializable: that is, is it a
/// first-class value type?
bool isMaterializable() const { return !(Bits & IsNotMaterializable); }
Expand Down Expand Up @@ -379,6 +386,11 @@ class alignas(1 << TypeAlignInBits) TypeBase {
return getRecursiveProperties().hasTypeVariable();
}

/// \brief Determine whether this type involves a UnresolvedType.
bool hasUnresolvedType() const {
return getRecursiveProperties().hasUnresolvedType();
}

/// Determine whether the type involves an archetype.
bool hasArchetype() const {
return getRecursiveProperties().hasArchetype();
Expand Down Expand Up @@ -818,6 +830,27 @@ class ErrorType : public TypeBase {
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(ErrorType, Type)

/// UnresolvedType - This represents a type variable that cannot be resolved to
/// a concrete type because the expression is ambiguous. This is produced when
/// parsing expressions and producing diagnostics. Any instance of this should
/// cause the entire expression to be ambiguously typed.
class UnresolvedType : public TypeBase {
friend class ASTContext;
// The Unresolved type is always canonical.
UnresolvedType(ASTContext &C)
: TypeBase(TypeKind::Unresolved, &C,
RecursiveTypeProperties(RecursiveTypeProperties::HasUnresolvedType)) { }
public:
static Type get(const ASTContext &C);

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
return T->getKind() == TypeKind::Unresolved;
}
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(UnresolvedType, Type)


/// BuiltinType - An abstract class for all the builtin types.
class BuiltinType : public TypeBase {
protected:
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts,
SwiftShimsModuleName(getIdentifier(SWIFT_SHIMS_NAME)),
TypeCheckerDebug(new StderrTypeCheckerDebugConsumer()),
TheErrorType(new (*this, AllocationArena::Permanent) ErrorType(*this)),
TheUnresolvedType(new (*this, AllocationArena::Permanent)
UnresolvedType(*this)),
TheEmptyTupleType(TupleType::get(ArrayRef<TupleTypeElt>(), *this)),
TheNativeObjectType(new (*this, AllocationArena::Permanent)
BuiltinNativeObjectType(*this)),
Expand All @@ -405,7 +407,7 @@ ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts,
TheIEEE128Type(new (*this, AllocationArena::Permanent)
BuiltinFloatType(BuiltinFloatType::IEEE128, *this)),
ThePPC128Type(new (*this, AllocationArena::Permanent)
BuiltinFloatType(BuiltinFloatType::PPC128,*this)) {
BuiltinFloatType(BuiltinFloatType::PPC128, *this)) {

// Initialize all of the known identifiers.
#define IDENTIFIER_WITH_NAME(Name, IdStr) Id_##Name = getIdentifier(IdStr);
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,7 @@ namespace {
}

TRIVIAL_TYPE_PRINTER(Error, error)
TRIVIAL_TYPE_PRINTER(Unresolved, unresolved)

void visitBuiltinIntegerType(BuiltinIntegerType *T, StringRef label) {
printCommon(T, label, "builtin_integer_type");
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,13 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
Printer << "<<error type>>";
}

void visitUnresolvedType(UnresolvedType *T) {
if (T->getASTContext().LangOpts.DebugConstraintSolver)
Printer << "<<unresolvedtype>>";
else
Printer << "_";
}

void visitBuiltinRawPointerType(BuiltinRawPointerType *T) {
Printer << "Builtin.RawPointer";
}
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Mangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ void Mangler::mangleType(Type type, ResilienceExpansion explosion,
llvm_unreachable("Cannot mangle module type yet");

case TypeKind::Error:
case TypeKind::Unresolved:
Buffer << "ERR";
return;

Expand Down
12 changes: 12 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,18 @@ LookupConformanceResult Module::lookupConformance(Type type,
}
}

// UnresolvedType is a placeholder for an unknown type used when generating
// diagnostics. We consider it to conform to all protocols, since the
// intended type might have.
if (type->is<UnresolvedType>()) {
return {
ctx.getConformance(type, protocol, protocol->getLoc(), this,
ProtocolConformanceState::Complete),
ConformanceKind::Conforms
};
}


auto nominal = type->getAnyNominal();

// If we don't have a nominal type, there are no conformances.
Expand Down
8 changes: 6 additions & 2 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ bool CanType::isReferenceTypeImpl(CanType type, bool functionsCount) {
// Nothing else is statically just a class reference.
case TypeKind::SILBlockStorage:
case TypeKind::Error:
case TypeKind::Unresolved:
case TypeKind::BuiltinInteger:
case TypeKind::BuiltinFloat:
case TypeKind::BuiltinRawPointer:
Expand Down Expand Up @@ -369,6 +370,7 @@ bool TypeBase::isUnspecializedGeneric() {
return false;

case TypeKind::Error:
case TypeKind::Unresolved:
case TypeKind::TypeVariable:
llvm_unreachable("querying invalid type");

Expand Down Expand Up @@ -1251,6 +1253,7 @@ CanType TypeBase::getCanonicalType() {
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"
case TypeKind::Error:
case TypeKind::Unresolved:
case TypeKind::TypeVariable:
llvm_unreachable("these types are always canonical");

Expand Down Expand Up @@ -2527,8 +2530,8 @@ static Type getMemberForBaseType(Module *module,
// conformances we're supposed to skip this conformance's unsatisfied type
// witnesses, and we have an unsatisfied type witness, return
// "missing".
if (conformance.getPointer() &&
conformance.getPointer()->getRootNormalConformance()->getState()
assert(conformance.getPointer());
if (conformance.getPointer()->getRootNormalConformance()->getState()
== ProtocolConformanceState::CheckingTypeWitnesses &&
!conformance.getPointer()->hasTypeWitness(assocType, nullptr))
return Type();
Expand Down Expand Up @@ -2777,6 +2780,7 @@ case TypeKind::Id:
#define TYPE(Id, Parent)
#include "swift/AST/TypeNodes.def"
case TypeKind::Error:
case TypeKind::Unresolved:
case TypeKind::TypeVariable:
case TypeKind::AssociatedType:
case TypeKind::GenericTypeParam:
Expand Down
1 change: 1 addition & 0 deletions lib/AST/TypeWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Traversal : public TypeVisitor<Traversal, bool>
TypeWalker &Walker;

bool visitErrorType(ErrorType *ty) { return false; }
bool visitUnresolvedType(UnresolvedType *ty) { return false; }
bool visitBuiltinType(BuiltinType *ty) { return false; }
bool visitNameAliasType(NameAliasType *ty) { return false; }

Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1805,6 +1805,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
// checker.
case TypeKind::AssociatedType:
case TypeKind::Error:
case TypeKind::Unresolved:
case TypeKind::LValue:
case TypeKind::TypeVariable:
case TypeKind::Module:
Expand Down
15 changes: 6 additions & 9 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2130,13 +2130,11 @@ namespace {
// Find the overload choice used for this declaration reference.
auto selected = getOverloadChoiceIfAvailable(locator);
if (!selected.hasValue()) {
assert(!expr->getDecl()->hasType() &&
isa<ParamDecl>(expr->getDecl()) &&
isa<ClosureExpr>(expr->getDecl()->getDeclContext()) &&
"The only case this should happen is for closure arguments in CSDiags");
return nullptr;
assert(expr->getDecl()->getType()->is<UnresolvedType>() &&
"should only happen for closure arguments in CSDiags");
expr->setType(expr->getDecl()->getType());
return expr;
}


auto choice = selected->choice;
auto decl = choice.getDecl();
Expand Down Expand Up @@ -5248,10 +5246,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
if (!fn)
return nullptr;

// Handle applications that implicitly look through ImplicitlyUnwrappedOptional<T>.
if (auto fnTy = cs.lookThroughImplicitlyUnwrappedOptionalType(fn->getType())) {
// Handle applications that look through ImplicitlyUnwrappedOptional<T>.
if (auto fnTy = cs.lookThroughImplicitlyUnwrappedOptionalType(fn->getType()))
fn = coerceImplicitlyUnwrappedOptionalToValue(fn, fnTy, locator);
}

// If we're applying a function that resulted from a covariant
// function conversion, strip off that conversion.
Expand Down
22 changes: 14 additions & 8 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1311,8 +1311,10 @@ namespace {
unsigned level;

AnyFunctionType *getUncurriedFunctionType() const {
#if 1 // TODO: Simplify with unresolved types? Can this be removed?
if (!decl->hasType())
return nullptr;
#endif

auto type = decl->getType();

Expand Down Expand Up @@ -2310,8 +2312,10 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
// ambiguous. Bail out so the general ambiguity diagnosing logic can handle
// it.
if (fromType->is<TypeVariableType>() ||
fromType->is<UnresolvedType>() ||
fromType->is<UnboundGenericType>() ||
toType->is<TypeVariableType>()) {
toType->is<TypeVariableType>() ||
toType->is<UnresolvedType>()) {
diagnoseAmbiguity(anchor);
return true;
}
Expand Down Expand Up @@ -2719,10 +2723,10 @@ typeCheckArbitrarySubExprIndependently(Expr *subExpr, TCCOptions options) {
// If we have a ClosureExpr parent of the specified node, check to make sure
// none of its arguments are type variables. If so, these type variables
// would be accessible to name lookup of the subexpression and may thus leak
// in. Reset them to null types for safe measures.
// in. Reset them to UnresolvedTypes for safe measures.
CE->getParams()->forEachVariable([&](VarDecl *VD) {
if (VD->getType()->hasTypeVariable() || VD->getType()->is<ErrorType>())
VD->overwriteType(Type());
VD->overwriteType(CS->getASTContext().TheUnresolvedType);
});
}

Expand Down Expand Up @@ -2895,7 +2899,8 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Type exprType) {
return;

// Don't take a constraint that won't tell us anything.
if (C->getSecondType()->hasTypeVariable())
if (C->getSecondType()->hasTypeVariable() ||
C->getSecondType()->hasUnresolvedType())
return;

foundConstraint = C;
Expand Down Expand Up @@ -3335,6 +3340,7 @@ bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) {
auto instanceTy =
SD->getGetter()->getImplicitSelfDecl()->getType()->getInOutObjectType();
if (!baseType->hasTypeVariable() &&
!baseType->hasUnresolvedType() &&
!CS->TC.isConvertibleTo(baseType, instanceTy, CS->DC)) {
selfConstraint = CC_SelfMismatch;
}
Expand Down Expand Up @@ -3679,11 +3685,10 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
// closure. These typevars come into them when the body does name
// lookups against the parameter decls.
//
// Handle this by rewriting the arguments to Type(). CSGen has special
// handling for ParamDecls from closure arguments with null types.
// Handle this by rewriting the arguments to UnresolvedType().
CE->getParams()->forEachVariable([&](VarDecl *VD) {
if (VD->getType()->hasTypeVariable() || VD->getType()->is<ErrorType>())
VD->overwriteType(Type());
VD->overwriteType(CS->getASTContext().TheUnresolvedType);
});
}

Expand Down Expand Up @@ -3863,7 +3868,8 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
// If this is a multi-statement closure with no explicit result type, emit
// a note to clue the developer in.
if (!CE->hasExplicitResultType() && CFTy &&
CFTy->getResult()->hasTypeVariable()) {
(CFTy->getResult()->hasTypeVariable() ||
CFTy->getResult()->is<UnresolvedType>())) {
diagnose(CE->getLoc(), diag::cannot_infer_closure_result_type);
return;
}
Expand Down
10 changes: 5 additions & 5 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,13 +1131,13 @@ namespace {
#endif // SWIFT_ENABLE_OBJECT_LITERALS

Type visitDeclRefExpr(DeclRefExpr *E) {
// If this is a ParamDecl for a closure argument that has a null type,
// then this is a situation where CSDiags is trying to perform
// If this is a ParamDecl for a closure argument that has an Unresolved
// type, then this is a situation where CSDiags is trying to perform
// error recovery within a ClosureExpr. Just create a new type variable
// for the decl that isn't bound to anything. This will guarantee that it
// for the decl that isn't bound to anything. This will ensure that it
// is considered ambiguous.
if (!E->getDecl()->hasType() && isa<ParamDecl>(E->getDecl()) &&
isa<ClosureExpr>(E->getDecl()->getDeclContext())) {
if (E->getDecl()->hasType() &&
E->getDecl()->getType()->is<UnresolvedType>()) {
return CS.createTypeVariable(CS.getConstraintLocator(E),
TVO_CanBindToLValue);
}
Expand Down
5 changes: 4 additions & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
return SolutionKind::Error;

case TypeKind::Error:
case TypeKind::Unresolved:
return SolutionKind::Error;

case TypeKind::GenericTypeParam:
Expand Down Expand Up @@ -2211,6 +2212,7 @@ ConstraintSystem::simplifyConstructionConstraint(Type valueType,
#include "swift/AST/TypeNodes.def"
llvm_unreachable("artificial type in constraint");

case TypeKind::Unresolved:
case TypeKind::Error:
return SolutionKind::Error;

Expand Down Expand Up @@ -2798,7 +2800,8 @@ performMemberLookup(Type baseTy, const Constraint &constraint) {

bool isExistential = instanceTy->isExistentialType();

if (instanceTy->is<TypeVariableType>()) {
if (instanceTy->is<TypeVariableType>() ||
instanceTy->is<UnresolvedType>()) {
MemberLookupResult result;
result.OverallResult = MemberLookupResult::Unsolved;
return result;
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ Solution ConstraintSystem::finalize(
case FreeTypeVariableBinding::GenericParameters:
assignFixedType(tv, GenericTypeParamType::get(0, index++, TC.Context));
break;

case FreeTypeVariableBinding::UnresolvedType:
assignFixedType(tv, TC.Context.TheUnresolvedType);
break;
}
}

Expand Down
Loading

0 comments on commit a899872

Please sign in to comment.