Skip to content
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
15 changes: 14 additions & 1 deletion include/swift/AST/Evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ class Evaluator {
/// is treated as a stack and is used to detect cycles.
llvm::SetVector<ActiveRequest> activeRequests;

/// A set of active requests that have been diagnosed for a cycle.
llvm::DenseSet<ActiveRequest> diagnosedActiveCycles;

/// A cache that stores the results of requests.
evaluator::RequestCache cache;

Expand Down Expand Up @@ -344,7 +347,17 @@ class Evaluator {

recorder.beginRequest<Request>();

auto result = getRequestFunction<Request>()(request, *this);
auto result = [&]() -> typename Request::OutputType {
auto reqResult = getRequestFunction<Request>()(request, *this);

// If we diagnosed a cycle for this request, we want to only use the
// default value to ensure we return a consistent result.
if (!diagnosedActiveCycles.empty() &&
diagnosedActiveCycles.erase(activeReq)) {
return defaultValueFn();
}
return reqResult;
}();

recorder.endRequest<Request>(request);

Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class GenericSignature {
ArrayRef<Requirement> requirements,
bool isKnownCanonical = false);

/// Create a new placeholder generic signature from a set of generic
/// parameters. This is necessary for recovery in invalid cases.
static GenericSignature forInvalid(ArrayRef<GenericTypeParamType *> params);

/// Produce a new generic signature which drops all of the marker
/// protocol conformance requirements associated with this one.
GenericSignature withoutMarkerProtocols() const;
Expand Down
31 changes: 23 additions & 8 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3618,15 +3618,11 @@ static Type replacingTypeVariablesAndPlaceholders(Type ty) {
Type ErrorType::get(Type originalType) {
// The original type is only used for printing/debugging, and we don't support
// solver-allocated ErrorTypes. As such, fold any type variables and
// placeholders into bare ErrorTypes, which print as placeholders.
//
// FIXME: If the originalType is itself an ErrorType we ought to be flattening
// it, but that's currently load-bearing as it avoids crashing for recursive
// generic signatures such as in `0120-rdar34184392.swift`. To fix this we
// ought to change the evaluator to ensure the outer step of a request cycle
// returns the same default value as the inner step such that we don't end up
// with conflicting generic signatures on encountering a cycle.
// placeholders into ErrorTypes. If we have a top-level one, we can return
// that directly.
originalType = replacingTypeVariablesAndPlaceholders(originalType);
if (isa<ErrorType>(originalType.getPointer()))
return originalType;

ASSERT(originalType);
ASSERT(!originalType->getRecursiveProperties().isSolverAllocated() &&
Expand Down Expand Up @@ -6007,6 +6003,25 @@ GenericSignature::get(ArrayRef<GenericTypeParamType *> params,
return newSig;
}

GenericSignature
GenericSignature::forInvalid(ArrayRef<GenericTypeParamType *> params) {
ASSERT(!params.empty());
auto &ctx = params.front()->getASTContext();

// Add same type requirements that make each of the generic parameters
// concrete error types. This helps avoid downstream diagnostics and is
// handled the same as if the user wrote e.g `<T where T == Undefined>`.
SmallVector<Requirement, 2> requirements;
for (auto *param : params) {
if (param->isValue())
continue;

requirements.emplace_back(RequirementKind::SameType, param,
ErrorType::get(ctx));
}
return GenericSignature::get(params, requirements);
}

GenericEnvironment *GenericEnvironment::forPrimary(GenericSignature signature) {
auto &ctx = signature->getASTContext();

Expand Down
25 changes: 10 additions & 15 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1643,8 +1643,7 @@ bool GenericContext::isComputingGenericSignature() const {

/// If we hit a cycle while building the generic signature, we can't return
/// nullptr, since this breaks invariants elsewhere. Instead, build a dummy
/// signature where everything is Copyable and Escapable, to avoid spurious
/// downstream diagnostics concerning move-only types.
/// invalid signature to avoid spurious downstream diagnostics.
static GenericSignature getPlaceholderGenericSignature(
ASTContext &ctx, const DeclContext *DC) {
SmallVector<GenericParamList *, 2> gpLists;
Expand All @@ -1660,22 +1659,13 @@ static GenericSignature getPlaceholderGenericSignature(
gpLists[i]->setDepth(i);

SmallVector<GenericTypeParamType *, 2> genericParams;
SmallVector<Requirement, 2> requirements;

for (auto *gpList : gpLists) {
for (auto *genericParam : *gpList) {
auto type = genericParam->getDeclaredInterfaceType();
genericParams.push_back(type->castTo<GenericTypeParamType>());

for (auto ip : InvertibleProtocolSet::allKnown()) {
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
requirements.emplace_back(RequirementKind::Conformance, type,
proto->getDeclaredInterfaceType());
}
}
}

return GenericSignature::get(genericParams, requirements);
return GenericSignature::forInvalid(genericParams);
}

GenericSignature GenericContext::getGenericSignature() const {
Expand Down Expand Up @@ -2700,10 +2690,16 @@ bool PatternBindingDecl::hasStorage() const {

const PatternBindingEntry *
PatternBindingDecl::getCheckedPatternBindingEntry(unsigned i) const {
return evaluateOrDefault(
auto result = evaluateOrDefault(
getASTContext().evaluator,
PatternBindingEntryRequest{const_cast<PatternBindingDecl *>(this), i},
nullptr);

// If we ran into a cycle, we can still return the entry.
if (!result)
return &getPatternList()[i];

return result;
}

void PatternBindingDecl::setPattern(unsigned i, Pattern *P,
Expand Down Expand Up @@ -7392,8 +7388,7 @@ bool ProtocolDecl::existentialConformsToSelf() const {
}

bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
// Because we will have considered all the protocols in a cyclic hierarchy by
// the time the cycle is hit.
// Be conservative and avoid diagnosing if we hit a cycle.
const bool resultForCycle = false;

return evaluateOrDefault(getASTContext().evaluator,
Expand Down
6 changes: 5 additions & 1 deletion lib/AST/Evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,12 @@ void Evaluator::diagnoseCycle(const ActiveRequest &request) {
request.diagnoseCycle(cycleDiags.getDiags());

for (const auto &step : llvm::reverse(activeRequests)) {
if (step == request)
if (step == request) {
// Note that we diagnosed a cycle for the outermost step to ensure it
// also returns a cyclic result.
diagnosedActiveCycles.insert(step);
return;
}
step.noteCycleStep(cycleDiags.getDiags());
}

Expand Down
22 changes: 1 addition & 21 deletions lib/AST/RequirementMachine/RequirementMachineRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,26 +750,6 @@ AbstractGenericSignatureRequest::evaluate(
}
}

/// If completion fails, build a dummy generic signature where everything is
/// Copyable and Escapable, to avoid spurious downstream diagnostics
/// concerning move-only types.
static GenericSignature getPlaceholderGenericSignature(
ASTContext &ctx, ArrayRef<GenericTypeParamType *> genericParams) {
SmallVector<Requirement, 2> requirements;
for (auto param : genericParams) {
if (param->isValue())
continue;

for (auto ip : InvertibleProtocolSet::allKnown()) {
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
requirements.emplace_back(RequirementKind::Conformance, param,
proto->getDeclaredInterfaceType());
}
}

return GenericSignature::get(genericParams, requirements);
}

GenericSignatureWithError
InferredGenericSignatureRequest::evaluate(
Evaluator &evaluator,
Expand Down Expand Up @@ -996,7 +976,7 @@ InferredGenericSignatureRequest::evaluate(
diag::requirement_machine_completion_rule,
rule);

auto result = getPlaceholderGenericSignature(ctx, genericParams);
auto result = GenericSignature::forInvalid(genericParams);

if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
rewriteCtx.endTimer("InferredGenericSignatureRequest");
Expand Down
7 changes: 2 additions & 5 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,9 +1917,6 @@ static Type replacePlaceholderType(PlaceholderType *placeholder,

return Type(gp);
});
if (isa<TypeVariableType>(replacement.getPointer()))
return ErrorType::get(ctx);

// For completion, we want to produce an archetype instead of an ErrorType
// for a top-level generic parameter.
// FIXME: This is pretty weird, we're producing a contextual type outside of
Expand All @@ -1930,8 +1927,8 @@ static Type replacePlaceholderType(PlaceholderType *placeholder,
return GP->getDecl()->getInnermostDeclContext()->mapTypeIntoContext(GP);
}
// Return an ErrorType with the replacement as the original type. Note that
// if we failed to replace a type variable with a generic parameter in a
// dependent member, `ErrorType::get` will fold it away.
// if we failed to replace a type variable with a generic parameter,
// `ErrorType::get` will fold it away.
return ErrorType::get(replacement);
}

Expand Down
25 changes: 14 additions & 11 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,8 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
return hadError;
}

bool TypeChecker::typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt) {
bool TypeChecker::typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt,
bool skipWhere) {
auto &Context = dc->getASTContext();
FrontendStatsTracer statsTracer(Context.Stats, "typecheck-for-each", stmt);
PrettyStackTraceStmt stackTrace(Context, "type-checking-for-each", stmt);
Expand All @@ -902,18 +903,20 @@ bool TypeChecker::typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt) {
if (!typeCheckTarget(target))
return true;

if (auto *where = stmt->getWhere()) {
auto boolType = dc->getASTContext().getBoolType();
if (!boolType)
return true;
if (!skipWhere) {
if (auto *where = stmt->getWhere()) {
auto boolType = dc->getASTContext().getBoolType();
if (!boolType)
return true;

SyntacticElementTarget whereClause(where, dc, {boolType, CTP_Condition},
/*isDiscarded=*/false);
auto result = typeCheckTarget(whereClause);
if (!result)
return true;
SyntacticElementTarget whereClause(where, dc, {boolType, CTP_Condition},
/*isDiscarded=*/false);
auto result = typeCheckTarget(whereClause);
if (!result)
return true;

stmt->setWhere(result->getAsExpr());
stmt->setWhere(result->getAsExpr());
}
}

// Check to see if the sequence expr is throwing (in async context),
Expand Down
13 changes: 9 additions & 4 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,17 +1156,22 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
getOrCreateRequirementEnvironment(reqEnvCache, dc, reqSig, proto,
covariantSelf, conformance);

auto reqSubMap = reqEnvironment.getRequirementToWitnessThunkSubs();

Type selfTy = proto->getSelfInterfaceType().subst(reqSubMap);
if (selfTy->hasError()) {
return RequirementMatch(witness, MatchKind::WitnessInvalid,
witnessType, reqEnvironment,
/*optionalAdjustments*/ {});
}

// Set up the constraint system for matching.
auto setup =
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type> {
// Construct a constraint system to use to solve the equality between
// the required type and the witness type.
cs.emplace(dc, ConstraintSystemFlags::AllowFixes);

auto reqSubMap = reqEnvironment.getRequirementToWitnessThunkSubs();

Type selfTy = proto->getSelfInterfaceType().subst(reqSubMap);

// Open up the type of the requirement.
reqLocator =
cs->getConstraintLocator(req, ConstraintLocator::ProtocolRequirement);
Expand Down
10 changes: 9 additions & 1 deletion lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,15 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
}

Stmt *visitForEachStmt(ForEachStmt *S) {
TypeChecker::typeCheckForEachPreamble(DC, S);
// If we're performing IDE inspection, we also want to skip the where
// clause if we're leaving the body unchecked.
// FIXME: This is a hack to avoid cycles through NamingPatternRequest when
// doing lazy type-checking, we ought to fix the request to be granular in
// the type-checking work it kicks.
bool skipWhere = LeaveBraceStmtBodyUnchecked &&
Ctx.SourceMgr.hasIDEInspectionTargetBuffer();

TypeChecker::typeCheckForEachPreamble(DC, S, skipWhere);

// Type-check the body of the loop.
auto sourceFile = DC->getParentSourceFile();
Expand Down
7 changes: 7 additions & 0 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ Type TypeResolution::resolveDependentMemberType(
// Record the type we found.
repr->setValue(nestedType, nullptr);
} else {
// If we have a concrete equivalence to an error type, avoid diagnosing the
// missing member.
if (auto concreteBase = genericSig->getConcreteType(baseTy)) {
if (concreteBase->hasError())
return ErrorType::get(baseTy);
}

// Resolve the base to a potential archetype.
// Perform typo correction.
TypoCorrectionResults corrections(repr->getNameRef(), repr->getNameLoc());
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,8 @@ bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber,
/// together.
///
/// \returns true if a failure occurred.
bool typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt);
bool typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt,
bool skipWhereClause);

/// Compute the set of captures for the given closure.
void computeCaptures(AbstractClosureExpr *ACE);
Expand Down
8 changes: 2 additions & 6 deletions test/Constraints/same_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,11 @@ protocol P2 {
}

// CHECK-LABEL: same_types.(file).structuralSameTypeRecursive1@
// CHECK-NEXT: Generic signature: <T, U>
// CHECK-NEXT: Generic signature: <T, U where T == <<error type>>, U == <<error type>>>

// expected-error@+2 {{cannot build rewrite system for generic signature; concrete type nesting limit exceeded}}
// expected-note@+1 {{τ_0_0.[P2:Assoc1].[concrete: ((((((((((((((((((((((((((((((((τ_0_0.[P2:Assoc1], τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1), τ_0_1)] => τ_0_0.[P2:Assoc1]}}
func structuralSameTypeRecursive1<T: P2, U>(_: T, _: U)
where T.Assoc1 == Tuple2<T.Assoc1, U>
// expected-error@-1 {{'Assoc1' is not a member type of type 'T'}}
// expected-error@-2 {{'Assoc1' is not a member type of type 'T'}}
{ }
func structuralSameTypeRecursive1<T: P2, U>(_: T, _: U) where T.Assoc1 == Tuple2<T.Assoc1, U> {}

protocol P3 {
}
Expand Down
10 changes: 10 additions & 0 deletions test/Generics/invalid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,13 @@ func errorMessageVariants(x: X<Int>, x2: X<Bool> = X<Int>()) -> X<Bool> {
let _: X<Int>.Foo = X<Bool>.Foo() // expected-error {{cannot convert parent type 'X<Bool>' to expected type 'X<Int>'}}
return x // expected-error {{cannot convert return expression of type 'X<Int>' to return type 'X<Bool>'}}
}

protocol P2 {
func foo()
}

// Make sure we don't diagnose the conformance failure or 'T.K'.
struct HasInvalidSameType<T>: P2 where T == Undefined, T.K == Int {
// expected-error@-1 {{cannot find type 'Undefined' in scope}}
func foo() {}
}
Loading