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
3 changes: 3 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,9 @@ class ASTContext final {
/// Get the '+' function on two String.
FuncDecl *getPlusFunctionOnString() const;

/// Get Sequence.makeIterator().
FuncDecl *getSequenceMakeIterator() const;

/// Check whether the standard library provides all the correct
/// intrinsic support for Optional<T>.
///
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3924,6 +3924,10 @@ class OpaqueValueExpr : public Expr {
/// value to be specified later.
bool isPlaceholder() const { return Bits.OpaqueValueExpr.IsPlaceholder; }

void setIsPlaceholder(bool value) {
Bits.OpaqueValueExpr.IsPlaceholder = value;
}

SourceRange getSourceRange() const { return Range; }

static bool classof(const Expr *E) {
Expand Down
8 changes: 0 additions & 8 deletions include/swift/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -808,8 +808,6 @@ class ForEachStmt : public LabeledStmt {

// Set by Sema:
ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef();
ConcreteDeclRef makeIterator;
ConcreteDeclRef iteratorNext;
VarDecl *iteratorVar = nullptr;
Expr *iteratorVarRef = nullptr;
OpaqueValueExpr *elementExpr = nullptr;
Expand Down Expand Up @@ -838,12 +836,6 @@ class ForEachStmt : public LabeledStmt {
void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; }
Expr *getConvertElementExpr() const { return convertElementExpr; }

void setMakeIterator(ConcreteDeclRef declRef) { makeIterator = declRef; }
ConcreteDeclRef getMakeIterator() const { return makeIterator; }

void setIteratorNext(ConcreteDeclRef declRef) { iteratorNext = declRef; }
ConcreteDeclRef getIteratorNext() const { return iteratorNext; }

void setSequenceConformance(ProtocolConformanceRef conformance) {
sequenceConformance = conformance;
}
Expand Down
28 changes: 28 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ struct ASTContext::Implementation {
/// The declaration of '+' function for two String.
FuncDecl *PlusFunctionOnString = nullptr;

/// The declaration of 'Sequence.makeIterator()'.
FuncDecl *MakeIterator = nullptr;

/// The declaration of Swift.Optional<T>.Some.
EnumElementDecl *OptionalSomeDecl = nullptr;

Expand Down Expand Up @@ -710,6 +713,31 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const {
return getImpl().PlusFunctionOnString;
}

FuncDecl *ASTContext::getSequenceMakeIterator() const {
if (getImpl().MakeIterator) {
return getImpl().MakeIterator;
}

auto proto = getProtocol(KnownProtocolKind::Sequence);
if (!proto)
return nullptr;

for (auto result : proto->lookupDirect(Id_makeIterator)) {
if (result->getDeclContext() != proto)
continue;

if (auto func = dyn_cast<FuncDecl>(result)) {
if (func->getParameters()->size() != 0)
continue;

getImpl().MakeIterator = func;
return func;
}
}

return nullptr;
}

#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
DECL_CLASS *ASTContext::get##NAME##Decl() const { \
if (getImpl().NAME##Decl) \
Expand Down
6 changes: 0 additions & 6 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1593,12 +1593,6 @@ class PrintStmt : public StmtVisitor<PrintStmt> {
}
void visitForEachStmt(ForEachStmt *S) {
printCommon(S, "for_each_stmt");
PrintWithColorRAII(OS, LiteralValueColor) << " make_generator=";
S->getMakeIterator().dump(
PrintWithColorRAII(OS, LiteralValueColor).getOS());
PrintWithColorRAII(OS, LiteralValueColor) << " next=";
S->getIteratorNext().dump(
PrintWithColorRAII(OS, LiteralValueColor).getOS());
OS << '\n';
printRec(S->getPattern());
OS << '\n';
Expand Down
38 changes: 35 additions & 3 deletions lib/SILGen/SILGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -903,14 +903,28 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) {
}

void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
// Dig out information about the sequence conformance.
auto sequenceConformance = S->getSequenceConformance();
Type sequenceType = S->getSequence()->getType();
auto sequenceProto =
SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence);
auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions(
sequenceProto, sequenceType, sequenceConformance);

// Emit the 'iterator' variable that we'll be using for iteration.
LexicalScope OuterForScope(SGF, CleanupLocation(S));
{
auto initialization =
SGF.emitInitializationForVarDecl(S->getIteratorVar(), false);
SILLocation loc = SILLocation(S->getSequence());

// Compute the reference to the Sequence's makeIterator().
FuncDecl *makeIteratorReq = SGF.getASTContext().getSequenceMakeIterator();
ConcreteDeclRef makeIteratorRef(makeIteratorReq, sequenceSubs);

// Call makeIterator().
RValue result = SGF.emitApplyMethod(
loc, S->getMakeIterator(), ArgumentSource(S->getSequence()),
loc, makeIteratorRef, ArgumentSource(S->getSequence()),
PreparedArguments(ArrayRef<AnyFunctionType::Param>({})),
SGFContext(initialization.get()));
if (!result.isInContext()) {
Expand Down Expand Up @@ -952,8 +966,26 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
JumpDest endDest = createJumpDest(S->getBody());
SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest });

// Compute the reference to the the iterator's next().
auto iteratorProto =
SGF.getASTContext().getProtocol(KnownProtocolKind::IteratorProtocol);
ValueDecl *iteratorNextReq = iteratorProto->getSingleRequirement(
DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next,
ArrayRef<Identifier>()));
auto iteratorAssocType =
sequenceProto->getAssociatedType(SGF.getASTContext().Id_Iterator);
auto iteratorMemberRef = DependentMemberType::get(
sequenceProto->getSelfInterfaceType(), iteratorAssocType);
auto iteratorType = sequenceConformance.getAssociatedType(
sequenceType, iteratorMemberRef);
auto iteratorConformance = sequenceConformance.getAssociatedConformance(
sequenceType, iteratorMemberRef, iteratorProto);
auto iteratorSubs = SubstitutionMap::getProtocolSubstitutions(
iteratorProto, iteratorType, iteratorConformance);
ConcreteDeclRef iteratorNextRef(iteratorNextReq, iteratorSubs);

auto buildArgumentSource = [&]() {
if (cast<FuncDecl>(S->getIteratorNext().getDecl())->getSelfAccessKind() ==
if (cast<FuncDecl>(iteratorNextRef.getDecl())->getSelfAccessKind() ==
SelfAccessKind::Mutating) {
LValue lv =
SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::ReadWrite);
Expand All @@ -969,7 +1001,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) {
RValue result;
result = SGF.emitApplyMethod(
loc, S->getIteratorNext(), buildArgumentSource(),
loc, iteratorNextRef, buildArgumentSource(),
PreparedArguments(ArrayRef<AnyFunctionType::Param>({})),
S->getElementExpr() ? SGFContext() : ctx);
if (S->getElementExpr()) {
Expand Down
32 changes: 32 additions & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Solution::computeSubstitutions(GenericSignature sig,
return ProtocolConformanceRef(protoType);
}

// FIXME: Retrieve the conformance from the solution itself.
return TypeChecker::conformsToProtocol(replacement, protoType,
getConstraintSystem().DC,
ConformanceCheckFlags::InExpression);
Expand Down Expand Up @@ -7345,6 +7346,37 @@ class SetExprTypes : public ASTWalker {
};
}

ProtocolConformanceRef Solution::resolveConformance(
ConstraintLocator *locator, ProtocolDecl *proto) {
for (const auto &conformance : Conformances) {
if (conformance.first != locator)
continue;
if (conformance.second.getRequirement() != proto)
continue;

// If the conformance doesn't require substitution, return it immediately.
auto conformanceRef = conformance.second;
if (conformanceRef.isAbstract())
return conformanceRef;

auto concrete = conformanceRef.getConcrete();
auto conformingType = concrete->getType();
if (!conformingType->hasTypeVariable())
return conformanceRef;

// Substitute into the conformance type, then look for a conformance
// again.
// FIXME: Should be able to perform the substitution using the Solution
// itself rather than another conforms-to-protocol check.
Type substConformingType = simplifyType(conformingType);
return TypeChecker::conformsToProtocol(
substConformingType, proto, constraintSystem->DC,
ConformanceCheckFlags::InExpression);
}

return ProtocolConformanceRef::forInvalid();
}

void Solution::setExprTypes(Expr *expr) const {
if (!expr)
return;
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {

case ConstraintKind::ValueMember:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueWitness:
// If our type variable shows up in the base type, there's
// nothing to do.
// FIXME: Can we avoid simplification here?
Expand Down
85 changes: 84 additions & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
Expand Down Expand Up @@ -1316,6 +1317,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
case ConstraintKind::OneWayEqual:
Expand Down Expand Up @@ -1594,6 +1596,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
Expand Down Expand Up @@ -3863,6 +3866,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
case ConstraintKind::OneWayEqual:
Expand Down Expand Up @@ -6079,7 +6083,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(

markMemberTypeAsPotentialHole(memberTy);
return SolutionKind::Solved;
} else if (kind == ConstraintKind::ValueMember && baseObjTy->isHole()) {
} else if ((kind == ConstraintKind::ValueMember ||
kind == ConstraintKind::ValueWitness) &&
baseObjTy->isHole()) {
// If base type is a "hole" there is no reason to record any
// more "member not found" fixes for chained member references.
increaseScore(SK_Fix);
Expand Down Expand Up @@ -6354,6 +6360,72 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
return SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyValueWitnessConstraint(
ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType,
DeclContext *useDC, FunctionRefKind functionRefKind,
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
// We'd need to record original base type because it might be a type
// variable representing another missing member.
auto origBaseType = baseType;

auto formUnsolved = [&] {
// If requested, generate a constraint.
if (flags.contains(TMF_GenerateConstraints)) {
auto *witnessConstraint = Constraint::createValueWitness(
*this, kind, origBaseType, memberType, requirement, useDC,
functionRefKind, getConstraintLocator(locator));

addUnsolvedConstraint(witnessConstraint);
return SolutionKind::Solved;
}

return SolutionKind::Unsolved;
};

// Resolve the base type, if we can. If we can't resolve the base type,
// then we can't solve this constraint.
Type baseObjectType = getFixedTypeRecursive(
baseType, flags, /*wantRValue=*/true);
if (baseObjectType->isTypeVariableOrMember()) {
return formUnsolved();
}

// Check conformance to the protocol. If it doesn't conform, this constraint
// fails. Don't attempt to fix it.
// FIXME: Look in the constraint system to see if we've resolved the
// conformance already?
auto proto = requirement->getDeclContext()->getSelfProtocolDecl();
assert(proto && "Value witness constraint for a non-requirement");
auto conformance = TypeChecker::conformsToProtocol(
baseObjectType, proto, useDC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::SkipConditionalRequirements));
if (!conformance) {
// The conformance failed, so mark the member type as a "hole". We cannot
// do anything further here.
if (!shouldAttemptFixes())
return SolutionKind::Error;

memberType.visit([&](Type type) {
if (auto *typeVar = type->getAs<TypeVariableType>())
recordPotentialHole(typeVar);
});

return SolutionKind::Solved;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to record AddMissingConformance fix here otherwise we might produce a solution with holes but without any fixes which would crash later in either verifier (in debug build) or SILGen (in release).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, okay. I guess it's fine to have redundant missing-conformance fixes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, they going to get coalesced into one for the same locator.

}

// Reference the requirement.
Type resolvedBaseType = simplifyType(baseType, flags);
if (resolvedBaseType->isTypeVariableOrMember())
return formUnsolved();

auto choice = OverloadChoice(resolvedBaseType, requirement, functionRefKind);
resolveOverload(getConstraintLocator(locator), memberType, choice,
useDC);
return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDefaultableConstraint(
Type first, Type second,
Expand Down Expand Up @@ -8671,6 +8743,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,

case ConstraintKind::ValueMember:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BindOverload:
case ConstraintKind::Disjunction:
case ConstraintKind::KeyPath:
Expand Down Expand Up @@ -9058,6 +9131,16 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
TMF_GenerateConstraints,
constraint.getLocator());

case ConstraintKind::ValueWitness:
return simplifyValueWitnessConstraint(constraint.getKind(),
constraint.getFirstType(),
constraint.getRequirement(),
constraint.getSecondType(),
constraint.getMemberUseDC(),
constraint.getFunctionRefKind(),
TMF_GenerateConstraints,
constraint.getLocator());

case ConstraintKind::Defaultable:
return simplifyDefaultableConstraint(constraint.getFirstType(),
constraint.getSecondType(),
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) {

case ConstraintKind::BindToPointerType:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::Disjunction:
case ConstraintKind::CheckedCast:
Expand Down
Loading