Skip to content
8 changes: 8 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7101,6 +7101,14 @@ class ParamDecl : public VarDecl {
Identifier parameterName, Expr *defaultValue,
DefaultArgumentInitializer *defaultValueInitContext, DeclContext *dc);

SourceLoc getLocFromSource() const {
// If we have a name loc, use it, otherwise fallback to the start loc for
// e.g enum elements without parameter names.
if (auto nameLoc = getNameLoc())
return nameLoc;
return getStartLoc();
}

/// Retrieve the argument (API) name for this function parameter.
Identifier getArgumentName() const {
return ArgumentNameAndFlags.getPointer();
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/TypeMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ class TypeMatcher {
#define SINGLETON_TYPE(SHORT_ID, ID) TRIVIAL_CASE(ID##Type)
#include "swift/AST/TypeNodes.def"

bool visitPlaceholderType(CanPlaceholderType firstType, Type secondType,
Type sugaredFirstType) {
// Placeholder types never match.
return mismatch(firstType.getPointer(), secondType, sugaredFirstType);
}

bool visitUnresolvedType(CanUnresolvedType firstType, Type secondType,
Type sugaredFirstType) {
// Unresolved types never match.
Expand Down
27 changes: 27 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ enum class FixKind : uint8_t {
/// resolved.
SpecifyTypeForPlaceholder,

/// Ignore an invalid placeholder in a decl's interface type.
IgnoreInvalidPlaceholderInDeclRef,

/// Allow Swift -> C pointer conversion in an argument position
/// of a Swift function.
AllowSwiftToCPointerConversion,
Expand Down Expand Up @@ -3191,6 +3194,30 @@ class SpecifyTypeForPlaceholder final : public ConstraintFix {
}
};

class IgnoreInvalidPlaceholderInDeclRef final : public ConstraintFix {
IgnoreInvalidPlaceholderInDeclRef(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::IgnoreInvalidPlaceholderInDeclRef, locator) {}

public:
std::string getName() const override {
return "ignore invalid placeholder in decl ref";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}

static IgnoreInvalidPlaceholderInDeclRef *create(ConstraintSystem &cs,
ConstraintLocator *locator);

static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::IgnoreInvalidPlaceholderInDeclRef;
}
};

class AllowRefToInvalidDecl final : public ConstraintFix {
AllowRefToInvalidDecl(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowRefToInvalidDecl, locator) {}
Expand Down
41 changes: 17 additions & 24 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4686,6 +4686,8 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
// For now, generic function types cannot be dependent (in fact,
// they erase dependence) or contain type variables, and they're
// always materializable.
// FIXME: This doesn't seem great, we should consider changing it to be opt-out
// rather than opt-in.
static RecursiveTypeProperties
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
Type result, Type globalActor,
Expand All @@ -4694,34 +4696,25 @@ getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
"revisit this if you add new recursive type properties");
RecursiveTypeProperties properties;

for (auto param : params) {
if (param.getPlainType()->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (param.getPlainType()->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}
using Prop = RecursiveTypeProperties::Property;
auto mask = (unsigned)Prop::HasError | Prop::IsUnsafe | Prop::HasPlaceholder;

auto unionBits = [&](Type ty) {
if (!ty)
return;
auto bits = ty->getRecursiveProperties().getBits();
properties |= Prop(bits & mask);
};

for (auto param : params)
unionBits(param.getPlainType());

if (result->getRecursiveProperties().hasDynamicSelf())
properties |= RecursiveTypeProperties::HasDynamicSelf;
if (result->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (result->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;

if (globalActor) {
if (globalActor->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (globalActor->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}

if (thrownError) {
if (thrownError->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
if (thrownError->getRecursiveProperties().isUnsafe())
properties |= RecursiveTypeProperties::IsUnsafe;
}

unionBits(result);
unionBits(globalActor);
unionBits(thrownError);
return properties;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/AssociatedTypeInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2317,9 +2317,9 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
/// Deduce associated types from dependent member types in the witness.
bool mismatch(DependentMemberType *firstDepMember,
TypeBase *secondType, Type sugaredFirstType) {
// If the second type is an error, don't look at it further, but proceed
// to find other matches.
if (secondType->hasError())
// If the second type is an error or placeholder, don't look at it
// further, but proceed to find other matches.
if (secondType->hasError() || secondType->hasPlaceholder())
return true;

// If the second type is a generic parameter of the witness, the match
Expand Down
14 changes: 14 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,20 @@ SpecifyTypeForPlaceholder::create(ConstraintSystem &cs,
return new (cs.getAllocator()) SpecifyTypeForPlaceholder(cs, locator);
}

IgnoreInvalidPlaceholderInDeclRef *
IgnoreInvalidPlaceholderInDeclRef::create(ConstraintSystem &cs, ConstraintLocator *locator) {
return new (cs.getAllocator()) IgnoreInvalidPlaceholderInDeclRef(cs, locator);
}

bool
IgnoreInvalidPlaceholderInDeclRef::diagnose(const Solution &solution,
bool asNote) const {
// These are diagnosed separately. Unfortunately we can't enforce that a
// diagnostic has already been emitted since their diagnosis depends on e.g
// type-checking a function body for a placeholder result of a function.
return true;
}

bool AllowRefToInvalidDecl::diagnose(const Solution &solution,
bool asNote) const {
ReferenceToInvalidDeclaration failure(solution, getLocator());
Expand Down
7 changes: 5 additions & 2 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2542,8 +2542,11 @@ namespace {
Type thrownErrorTy = [&] {
// Explicitly-specified thrown type.
if (closure->getExplicitThrownTypeRepr()) {
if (Type explicitType = closure->getExplicitThrownType())
return explicitType;
if (Type explicitType = closure->getExplicitThrownType()) {
// The thrown type may have errors, open as placeholders if needed.
return CS.replaceInferableTypesWithTypeVars(explicitType,
thrownErrorLocator);
}
}

// Explicitly-specified 'throws' without a type is untyped throws.
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16109,6 +16109,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
case FixKind::AllowValueExpansionWithoutPackReferences:
case FixKind::IgnoreInvalidPatternInExpr:
case FixKind::IgnoreInvalidPlaceholder:
case FixKind::IgnoreInvalidPlaceholderInDeclRef:
case FixKind::IgnoreOutOfPlaceThenStmt:
case FixKind::IgnoreMissingEachKeyword:
case FixKind::AllowInlineArrayLiteralCountMismatch:
Expand Down
43 changes: 30 additions & 13 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2121,10 +2121,17 @@ ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (decl->preconcurrency())
options |= TypeResolutionFlags::Preconcurrency;

// Placeholders are only currently allowed for FuncDecls with bodies, which
// we diagnose in ReturnTypePlaceholderReplacer.
HandlePlaceholderTypeReprFn placeholderOpener;
if (auto *FD = dyn_cast<FuncDecl>(decl)) {
if (FD->hasBody() && !FD->isBodySkipped())
placeholderOpener = PlaceholderType::get;
}
auto *const dc = decl->getInnermostDeclContext();
return TypeResolution::forInterface(dc, options,
/*unboundTyOpener*/ nullptr,
PlaceholderType::get,
placeholderOpener,
/*packElementOpener*/ nullptr)
.resolveType(resultTyRepr);
}
Expand Down Expand Up @@ -2310,9 +2317,17 @@ static Type validateParameterType(ParamDecl *decl) {
: TypeResolverContext::FunctionInput);
options |= TypeResolutionFlags::Direct;

// We allow placeholders in parameter types to improve recovery since if a
// default argument is present we can suggest the inferred type. Avoid doing
// this for protocol requirements though since those can't ever have default
// arguments anyway.
HandlePlaceholderTypeReprFn placeholderOpener;
if (!isa<ProtocolDecl>(dc->getParent()))
placeholderOpener = PlaceholderType::get;

const auto resolution =
TypeResolution::forInterface(dc, options, unboundTyOpener,
PlaceholderType::get,
placeholderOpener,
/*packElementOpener*/ nullptr);

if (isa<VarargTypeRepr>(nestedRepr)) {
Expand Down Expand Up @@ -3026,9 +3041,11 @@ bool TypeChecker::isPassThroughTypealias(TypeAliasDecl *typealias,

Type
ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
auto error = [&ext]() {
auto &ctx = ext->getASTContext();

auto error = [&]() {
ext->setInvalid();
return ErrorType::get(ext->getASTContext());
return ErrorType::get(ctx);
};

// If we didn't parse a type, fill in an error type and bail out.
Expand All @@ -3052,6 +3069,15 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
if (extendedType->hasError())
return error();

auto &diags = ctx.Diags;

// Cannot extend types who contain placeholders.
if (extendedType->hasPlaceholder()) {
diags.diagnose(ext->getLoc(), diag::extension_placeholder)
.highlight(extendedRepr->getSourceRange());
return error();
}

// Hack to allow extending a generic typealias.
if (auto *unboundGeneric = extendedType->getAs<UnboundGenericType>()) {
if (auto *aliasDecl = dyn_cast<TypeAliasDecl>(unboundGeneric->getDecl())) {
Expand All @@ -3069,8 +3095,6 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
}
}

auto &diags = ext->getASTContext().Diags;

// Cannot extend a metatype.
if (extendedType->is<AnyMetatypeType>()) {
diags.diagnose(ext->getLoc(), diag::extension_metatype, extendedType)
Expand All @@ -3087,13 +3111,6 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
return error();
}

// Cannot extend types who contain placeholders.
if (extendedType->hasPlaceholder()) {
diags.diagnose(ext->getLoc(), diag::extension_placeholder)
.highlight(extendedRepr->getSourceRange());
return error();
}

return extendedType;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6999,7 +6999,7 @@ Type ExplicitCaughtTypeRequest::evaluate(

return TypeResolution::forInterface(func, options,
/*unboundTyOpener*/ nullptr,
PlaceholderType::get,
/*placeholderOpener*/ nullptr,
/*packElementOpener*/ nullptr)
.resolveType(thrownTypeRepr);
}
Expand All @@ -7011,7 +7011,7 @@ Type ExplicitCaughtTypeRequest::evaluate(
return TypeResolution::resolveContextualType(
thrownTypeRepr, closure,
TypeResolutionOptions(TypeResolverContext::None),
/*unboundTyOpener*/ nullptr, PlaceholderType::get,
/*unboundTyOpener*/ nullptr, /*placeholderOpener*/ nullptr,
/*packElementOpener*/ nullptr);
}

Expand Down Expand Up @@ -7045,7 +7045,7 @@ Type ExplicitCaughtTypeRequest::evaluate(
return TypeResolution::resolveContextualType(
typeRepr, doCatch->getDeclContext(),
TypeResolutionOptions(TypeResolverContext::None),
/*unboundTyOpener*/ nullptr, PlaceholderType::get,
/*unboundTyOpener*/ nullptr, /*placeholderOpener*/ nullptr,
/*packElementOpener*/ nullptr);
}

Expand Down
27 changes: 27 additions & 0 deletions lib/Sema/TypeOfReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1080,13 +1080,39 @@ static Type replaceParamErrorTypeByPlaceholder(Type type, ValueDecl *value, bool
funcType->getExtInfo());
}

/// We allow placeholder types in interface types in certain cases to allow
/// better recovery since we can suggest the inferred type as a replacement.
/// When referencing those decls though we need to make sure we record a fix
/// since we cannot form a valid solution with them.
static void
recordFixIfNeededForPlaceholderInDecl(ConstraintSystem &cs, ValueDecl *D,
ConstraintLocatorBuilder locator) {
auto mayHavePlaceholder = [&]() -> bool {
// Parameters with types may have placeholders, since this allows us to
// suggest an inferred type from a default argument. Match the logic in
// `getUnopenedTypeOfReference` and only query the interface type if we have
// one already.
if (auto *PD = dyn_cast<ParamDecl>(D))
return PD->hasInterfaceType();

// Therefore decls with parameter lists may also have placeholders.
return D->getParameterList();
};
if (!mayHavePlaceholder() || !D->getInterfaceType()->hasPlaceholder())
return;

auto *loc = cs.getConstraintLocator(locator);
cs.recordFix(IgnoreInvalidPlaceholderInDeclRef::create(cs, loc));
}

DeclReferenceType
ConstraintSystem::getTypeOfReference(ValueDecl *value,
FunctionRefInfo functionRefInfo,
ConstraintLocatorBuilder locator,
DeclContext *useDC,
PreparedOverloadBuilder *preparedOverload) {
ASSERT(!!preparedOverload == PreparingOverload);
recordFixIfNeededForPlaceholderInDecl(*this, value, locator);

if (value->getDeclContext()->isTypeContext() && isa<FuncDecl>(value)) {
// Unqualified lookup can find operator names within nominal types.
Expand Down Expand Up @@ -1777,6 +1803,7 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
SmallVectorImpl<OpenedType> *replacementsPtr,
PreparedOverloadBuilder *preparedOverload) {
ASSERT(!!preparedOverload == PreparingOverload);
recordFixIfNeededForPlaceholderInDecl(*this, value, locator);

// Figure out the instance type used for the base.
Type baseRValueTy = baseTy->getRValueType();
Expand Down
Loading