Skip to content

Commit ec5cebd

Browse files
committed
IUO: Update protocol conformance checking to check IUO attr on decl.
1 parent d289554 commit ec5cebd

File tree

1 file changed

+83
-21
lines changed

1 file changed

+83
-21
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,10 @@ namespace {
133133
};
134134
} // end anonymous namespace
135135

136-
static std::tuple<Type,Type, OptionalAdjustmentKind>
137-
getTypesToCompare(ValueDecl *reqt,
138-
Type reqtType,
139-
Type witnessType,
140-
VarianceKind variance) {
136+
static std::tuple<Type, Type, OptionalAdjustmentKind>
137+
getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
138+
Type witnessType, bool witnessTypeIsIUO,
139+
VarianceKind variance) {
141140
// For @objc protocols, deal with differences in the optionality.
142141
// FIXME: It probably makes sense to extend this to non-@objc
143142
// protocols as well, but this requires more testing.
@@ -160,6 +159,11 @@ getTypesToCompare(ValueDecl *reqt,
160159
break;
161160

162161
case OTK_Optional:
162+
if (witnessTypeIsIUO) {
163+
optAdjustment = OptionalAdjustmentKind::RemoveIUO;
164+
break;
165+
}
166+
163167
switch (variance) {
164168
case VarianceKind::None:
165169
case VarianceKind::Covariant:
@@ -179,6 +183,17 @@ getTypesToCompare(ValueDecl *reqt,
179183
break;
180184

181185
case OTK_Optional:
186+
// When the requirement is an IUO, all is permitted, because we
187+
// assume that the user knows more about the signature than we
188+
// have information in the protocol.
189+
if (reqtTypeIsIUO)
190+
break;
191+
192+
if (witnessTypeIsIUO) {
193+
optAdjustment = OptionalAdjustmentKind::IUOToOptional;
194+
break;
195+
}
196+
182197
switch (witnessOptKind) {
183198
case OTK_None:
184199
switch (variance) {
@@ -209,6 +224,15 @@ getTypesToCompare(ValueDecl *reqt,
209224
// have information in the protocol.
210225
break;
211226
}
227+
} else {
228+
// FIXME: Until IUOs are removed from the type system, make
229+
// sure we turn these both into optionals for the purpose of
230+
// comparing types since we could be producing IUOs in some
231+
// places and optionals in others.
232+
if (auto objTy = reqtType->getImplicitlyUnwrappedOptionalObjectType())
233+
reqtType = OptionalType::get(objTy);
234+
if (auto objTy = witnessType->getImplicitlyUnwrappedOptionalObjectType())
235+
witnessType = OptionalType::get(objTy);
212236
}
213237

214238
return std::make_tuple(reqtType, witnessType, optAdjustment);
@@ -344,6 +368,14 @@ static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req,
344368
return false;
345369
}
346370

371+
static ParameterList *getParameterList(ValueDecl *value) {
372+
if (auto func = dyn_cast<AbstractFunctionDecl>(value))
373+
return func->getParameterList(func->getDeclContext()->isTypeContext());
374+
375+
auto subscript = cast<SubscriptDecl>(value);
376+
return subscript->getIndices();
377+
}
378+
347379
RequirementMatch
348380
swift::matchWitness(
349381
TypeChecker &tc,
@@ -491,18 +523,24 @@ swift::matchWitness(
491523
// Result types must match.
492524
// FIXME: Could allow (trivial?) subtyping here.
493525
if (!ignoreReturnType) {
494-
auto types = getTypesToCompare(req, reqResultType,
495-
witnessResultType,
496-
VarianceKind::Covariant);
526+
auto reqTypeIsIUO =
527+
req->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
528+
auto witnessTypeIsIUO =
529+
witness->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
530+
auto types =
531+
getTypesToCompare(req, reqResultType, reqTypeIsIUO, witnessResultType,
532+
witnessTypeIsIUO, VarianceKind::Covariant);
497533

498534
// Record optional adjustment, if any.
499535
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
500536
optionalAdjustments.push_back(
501537
OptionalAdjustment(std::get<2>(types)));
502538
}
503539

504-
if (auto result = matchTypes(std::get<0>(types),
505-
std::get<1>(types))) {
540+
if (!req->isObjC() && reqTypeIsIUO != witnessTypeIsIUO)
541+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
542+
543+
if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
506544
return std::move(result.getValue());
507545
}
508546
}
@@ -518,6 +556,12 @@ swift::matchWitness(
518556
return RequirementMatch(witness, MatchKind::TypeConflict,
519557
witnessType);
520558

559+
ParameterList *witnessParamList = getParameterList(witness);
560+
assert(witnessParamList->size() == witnessParams.size());
561+
562+
ParameterList *reqParamList = getParameterList(req);
563+
assert(reqParamList->size() == reqParams.size());
564+
521565
// Match each of the parameters.
522566
for (unsigned i = 0, n = reqParams.size(); i != n; ++i) {
523567
// Variadic bits must match.
@@ -531,21 +575,33 @@ swift::matchWitness(
531575
if (reqParams[i].isInOut() != witnessParams[i].isInOut())
532576
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
533577

578+
auto reqParamDecl = reqParamList->get(i);
579+
auto witnessParamDecl = witnessParamList->get(i);
580+
581+
auto reqParamTypeIsIUO =
582+
reqParamDecl->getAttrs()
583+
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
584+
auto witnessParamTypeIsIUO =
585+
witnessParamDecl->getAttrs()
586+
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
587+
534588
// Gross hack: strip a level of unchecked-optionality off both
535589
// sides when matching against a protocol imported from Objective-C.
536-
auto types = getTypesToCompare(req, reqParams[i].getType(),
537-
witnessParams[i].getType(),
538-
VarianceKind::Contravariant);
590+
auto types =
591+
getTypesToCompare(req, reqParams[i].getType(), reqParamTypeIsIUO,
592+
witnessParams[i].getType(), witnessParamTypeIsIUO,
593+
VarianceKind::Contravariant);
539594

540595
// Record any optional adjustment that occurred.
541596
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
542597
optionalAdjustments.push_back(
543598
OptionalAdjustment(std::get<2>(types), i));
544599
}
545600

546-
// Check whether the parameter types match.
547-
if (auto result = matchTypes(std::get<0>(types),
548-
std::get<1>(types))) {
601+
if (!req->isObjC() && reqParamTypeIsIUO != witnessParamTypeIsIUO)
602+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
603+
604+
if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
549605
return std::move(result.getValue());
550606
}
551607
}
@@ -557,16 +613,22 @@ swift::matchWitness(
557613
}
558614

559615
} else {
560-
// Simple case: add the constraint.
561-
auto types = getTypesToCompare(req, reqType, witnessType,
562-
VarianceKind::None);
616+
auto reqTypeIsIUO =
617+
req->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
618+
auto witnessTypeIsIUO =
619+
witness->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
620+
auto types = getTypesToCompare(req, reqType, reqTypeIsIUO, witnessType,
621+
witnessTypeIsIUO, VarianceKind::None);
563622

564623
// Record optional adjustment, if any.
565624
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
566625
optionalAdjustments.push_back(
567626
OptionalAdjustment(std::get<2>(types)));
568627
}
569628

629+
if (!req->isObjC() && reqTypeIsIUO != witnessTypeIsIUO)
630+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
631+
570632
if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
571633
return std::move(result.getValue());
572634
}
@@ -724,8 +786,8 @@ RequirementMatch swift::matchWitness(TypeChecker &tc,
724786
};
725787

726788
// Match a type in the requirement to a type in the witness.
727-
auto matchTypes = [&](Type reqType, Type witnessType)
728-
-> Optional<RequirementMatch> {
789+
auto matchTypes = [&](Type reqType,
790+
Type witnessType) -> Optional<RequirementMatch> {
729791
cs->addConstraint(ConstraintKind::Equal, reqType, witnessType, locator);
730792
// FIXME: Check whether this has already failed.
731793
return None;

0 commit comments

Comments
 (0)