[CS] Replace UnresolvedType with ErrorType in simplifyType/resolveType

This means we now either produce a bare ErrorType, or an ErrorType
with a generic parameter original type for a generic parameter hole.
We ought to further consolidate this logic by sinking the generic
parameter original type replacement into `simplifyType` itself, but
I'm leaving that for a future patch since it affects completion
results and I want to try keep this close to NFC.
This commit is contained in:
Hamish Knight
2025-10-01 22:30:46 +01:00
parent 21141f466c
commit 5171b84dba
11 changed files with 36 additions and 34 deletions

View File

@@ -983,11 +983,10 @@ static void formatDiagnosticArgument(StringRef Modifier,
needsQualification = typeSpellingIsAmbiguous(type, Args, printOptions);
}
// If a type has an unresolved type, print it with syntax sugar removed for
// If a type has a bare error type, print it with syntax sugar removed for
// clarity. For example, print `Array<_>` instead of `[_]`.
if (type->hasUnresolvedType()) {
if (type->hasBareError())
type = type->getWithoutSyntaxSugar();
}
if (needsQualification &&
isa<OpaqueTypeArchetypeType>(type.getPointer()) &&

View File

@@ -201,7 +201,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
}
}
if (ExpectedCallType &&
(ExpectedCallType->hasUnresolvedType() ||
(ExpectedCallType->hasError() ||
ExpectedCallType->hasUnboundGenericType())) {
ExpectedCallType = Type();
}

View File

@@ -693,7 +693,7 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD, Type ExprType) {
// For a GenericFunctionType, we only want to substitute the
// param/result types, as otherwise we might end up with a bad generic
// signature if there are UnresolvedTypes present in the base type. Note
// signature if there are ErrorTypes present in the base type. Note
// we pass in DesugarMemberTypes so that we see the actual concrete type
// witnesses instead of type alias types.
if (auto *GFT = T->getAs<GenericFunctionType>()) {

View File

@@ -123,7 +123,7 @@ void ConformingMethodListCallbacks::readyForTypeChecking(SourceFile *SrcFile) {
Type T = Res.Ty;
WithSolutionSpecificVarTypesRAII VarType(Res.SolutionSpecificVarTypes);
if (!T || T->is<ErrorType>() || T->is<UnresolvedType>())
if (!T || T->is<ErrorType>())
return;
T = T->getRValueType();

View File

@@ -420,7 +420,7 @@ void PostfixCompletionCallback::collectResults(
// tuple. But that doesnt really make sense so we shouldn't be suggesting
// any operators based on `Void`.
if (IncludeOperators && !Result.BaseIsStaticMetaType &&
!Result.BaseTy->isVoid() &&
!Result.BaseTy->isVoid() && !Result.BaseTy->hasError() &&
!ProcessedBaseTypes.contains(Result.BaseTy)) {
addOperatorResults(Result.BaseTy, Operators, DC, Lookup);
}

View File

@@ -56,7 +56,7 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
OverloadChoiceKind::KeyPathApplication) {
auto Params = Result.ValueTy->getAs<AnyFunctionType>()->getParams();
if (Params.size() == 1 &&
Params[0].getPlainType()->is<UnresolvedType>()) {
Params[0].getPlainType()->is<ErrorType>()) {
auto *KPDecl = CS.getASTContext().getKeyPathDecl();
Type KPTy =
KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType());

View File

@@ -27,7 +27,7 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S,
// Use the contextual type, unless it is still unresolved, in which case fall
// back to getting the type from the expression.
if (auto ContextualType = S.getContextualType(Node)) {
if (!ContextualType->hasUnresolvedType() &&
if (!ContextualType->hasError() &&
!ContextualType->hasUnboundGenericType()) {
return ContextualType;
}
@@ -46,7 +46,7 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S,
Result = S.getResolvedType(Node);
}
if (Result && Result->is<UnresolvedType>()) {
if (Result && Result->is<ErrorType>()) {
Result = Type();
}
return Result;

View File

@@ -129,7 +129,7 @@ void ContextInfoCallbacks::readyForTypeChecking(SourceFile *SrcFile) {
SmallVector<TypeContextInfoItem, 2> results;
for (auto T : TypeCheckCallback.getTypes()) {
if (T->is<ErrorType>() || T->is<UnresolvedType>())
if (T->is<ErrorType>())
continue;
T = T->getRValueType();

View File

@@ -105,15 +105,16 @@ Solution::computeSubstitutions(NullablePtr<ValueDecl> decl,
if (openedTypes == OpenedTypes.end())
return SubstitutionMap();
auto &ctx = getConstraintSystem().getASTContext();
SmallVector<Type, 4> replacementTypes;
for (const auto &opened : openedTypes->second) {
auto type = getFixedType(opened.second);
if (opened.first->isParameterPack()) {
if (type->is<PlaceholderType>()) {
auto &ctx = type->getASTContext();
type =
PackType::get(ctx, {PackExpansionType::get(ctx.TheUnresolvedType,
ctx.TheUnresolvedType)});
type = PackType::get(
ctx,
{PackExpansionType::get(ErrorType::get(ctx), ErrorType::get(ctx))});
} else if (!type->is<PackType>())
type = PackType::getSingletonPackExpansion(type);
}
@@ -126,8 +127,11 @@ Solution::computeSubstitutions(NullablePtr<ValueDecl> decl,
auto replacement = original.subst(IFS);
assert(!replacement->is<GenericTypeParamType>());
if (replacement->hasError() ||
isOpenedAnyObject(replacement) ||
if (replacement->hasError()) {
return ProtocolConformanceRef::forAbstract(ErrorType::get(replacement),
protoType);
}
if (isOpenedAnyObject(replacement) ||
replacement->is<GenericTypeParamType>()) {
return ProtocolConformanceRef::forAbstract(replacement, protoType);
}
@@ -7218,7 +7222,7 @@ Expr *ConstraintSystem::addImplicitLoadExpr(Expr *expr) {
Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
ConstraintLocatorBuilder locator) {
ASSERT(toType && !toType->hasError() && !toType->hasUnresolvedType() &&
ASSERT(toType && !toType->hasError() &&
!toType->hasTypeVariableOrPlaceholder());
// Diagnose conversions to invalid function types that couldn't be performed
@@ -10032,8 +10036,7 @@ ConstraintSystem::applySolution(Solution &solution,
// unresolved types.
{
auto isValidType = [&](Type ty) {
return !ty->hasUnresolvedType() && !ty->hasError() &&
!ty->hasTypeVariableOrPlaceholder();
return !ty->hasError() && !ty->hasTypeVariableOrPlaceholder();
};
for (auto &[_, type] : solution.typeBindings) {
ASSERT(isValidType(type) && "type binding has invalid type");

View File

@@ -100,7 +100,7 @@ Type FailureDiagnostic::resolveType(Type rawType, bool reconstituteSugar,
if (auto *typeVar = type->getAs<TypeVariableType>()) {
auto resolvedType = S.simplifyType(typeVar);
if (!resolvedType->hasUnresolvedType())
if (!resolvedType->hasError())
return resolvedType;
// If type variable was simplified to an unresolved pack expansion
@@ -117,7 +117,7 @@ Type FailureDiagnostic::resolveType(Type rawType, bool reconstituteSugar,
}
Type GP = typeVar->getImpl().getGenericParameter();
return resolvedType->is<UnresolvedType>() && GP
return resolvedType->is<ErrorType>() && GP
? ErrorType::get(GP)
: resolvedType;
}
@@ -128,7 +128,7 @@ Type FailureDiagnostic::resolveType(Type rawType, bool reconstituteSugar,
}
if (type->isPlaceholder())
return Type(type->getASTContext().TheUnresolvedType);
return ErrorType::get(type->getASTContext());
return std::nullopt;
});
@@ -183,7 +183,7 @@ StringRef FailureDiagnostic::getEditorPlaceholder(
llvm::SmallVectorImpl<char> &scratch) const {
llvm::raw_svector_ostream OS(scratch);
OS << "<#";
if (!ty || ty->is<UnresolvedType>()) {
if (!ty || ty->isBareErrorType()) {
OS << description;
} else {
OS << "T##";
@@ -5681,7 +5681,7 @@ bool MissingArgumentsFailure::diagnoseMissingResultBuilderElement() const {
auto fixIt = getEditorPlaceholder("result", paramType, scratch);
auto fixItLoc = call->getStartLoc();
if (paramType->is<UnresolvedType>()) {
if (paramType->isBareErrorType()) {
emitDiagnostic(diag::result_builder_missing_element,
resultBuilder->getName())
.fixItInsertAfter(fixItLoc, fixIt);
@@ -5804,7 +5804,7 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument(
auto argType = solution.simplifyType(solution.getType(unaryArg));
auto paramType = fnType->getParams()[1].getPlainType();
if (isExpr<ClosureExpr>(unaryArg) && argType->is<UnresolvedType>()) {
if (isExpr<ClosureExpr>(unaryArg) && argType->is<ErrorType>()) {
auto unwrappedParamTy = paramType->lookThroughAllOptionalTypes();
if (unwrappedParamTy->is<FunctionType>() || unwrappedParamTy->isAny())
return true;
@@ -5958,7 +5958,7 @@ bool ClosureParamDestructuringFailure::diagnoseAsError() {
};
auto isValidType = [](Type resultType) -> bool {
return resultType && !resultType->hasUnresolvedType() &&
return resultType && !resultType->hasError() &&
!resultType->hasTypeVariable();
};
@@ -6619,7 +6619,7 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
// statement it has to be diagnosed as pattern match if there are
// holes present in the contextual type.
if (purpose == ContextualTypePurpose::CTP_ForEachSequence &&
contextualType->hasUnresolvedType()) {
contextualType->hasError()) {
auto diagnostic = emitDiagnostic(
(contextualType->is<TupleType>() && !eltType->is<TupleType>())
? diag::cannot_match_expr_tuple_pattern_with_nontuple_value
@@ -7490,7 +7490,7 @@ bool ArgumentMismatchFailure::diagnoseAsError() {
if (argType->isKeyPath() && !paramType->isKnownKeyPathType()) {
auto keyPathTy = argType->castTo<BoundGenericType>();
auto rootTy = keyPathTy->getGenericArgs()[0];
if (rootTy->is<UnresolvedType>()) {
if (rootTy->isBareErrorType()) {
emitDiagnostic(diag::cannot_convert_unresolved_key_path_argument_value,
paramType);
return true;
@@ -7627,7 +7627,7 @@ bool ArgumentMismatchFailure::diagnosePatternMatchingMismatch() const {
auto rhsType = getType(rhsExpr);
auto diagnostic =
lhsType->is<UnresolvedType>()
lhsType->isBareErrorType()
? emitDiagnostic(
diag::cannot_match_unresolved_expr_pattern_with_value, rhsType)
: emitDiagnostic(diag::cannot_match_expr_pattern_with_value, lhsType,
@@ -8286,7 +8286,7 @@ bool UnableToInferClosureParameterType::diagnoseAsError() {
if (parentExpr) {
// Missing or invalid member reference in call.
if (auto *AE = dyn_cast<ApplyExpr>(parentExpr)) {
if (getType(AE->getFn())->is<UnresolvedType>())
if (getType(AE->getFn())->is<ErrorType>())
return false;
}

View File

@@ -1886,7 +1886,7 @@ Type Solution::simplifyType(Type type, bool wantInterfaceType) const {
ASSERT(!(wantInterfaceType && resolvedType->hasPrimaryArchetype()));
// We may have type variables and placeholders left over. These are solver
// allocated so cannot escape this function. Turn them into UnresolvedType.
// allocated so cannot escape this function. Turn them into ErrorType.
// - Type variables may still be present from unresolved pack expansions where
// e.g the count type is a hole, so the pattern may never become a
// concrete type.
@@ -1900,7 +1900,7 @@ Type Solution::simplifyType(Type type, bool wantInterfaceType) const {
auto *typePtr = type.getPointer();
if (isa<TypeVariableType>(typePtr) || isa<PlaceholderType>(typePtr))
return Type(ctx.TheUnresolvedType);
return ErrorType::get(ctx);
return std::nullopt;
});
@@ -4298,7 +4298,7 @@ Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
// If callee couldn't be resolved due to expression
// issues e.g. it's a reference to an invalid member
// let's just return here.
if (simplifyType(rawFnType)->is<UnresolvedType>())
if (simplifyType(rawFnType)->is<ErrorType>())
return std::nullopt;
// A tuple construction is spelled in the AST as a function call, but