Skip to content

Commit 7bc5ac1

Browse files
committed
Sema: Factor out ConformanceChecker::checkNonFinalClassWitness() from resolveWitnessViaLookup()
1 parent 5afaadf commit 7bc5ac1

File tree

1 file changed

+117
-105
lines changed

1 file changed

+117
-105
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 117 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,11 @@ namespace {
19411941
void recordTypeWitness(AssociatedTypeDecl *assocType, Type type,
19421942
TypeDecl *typeDecl, bool performRedeclarationCheck);
19431943

1944+
/// Enforce restrictions on non-final classes witnessing requirements
1945+
/// involving the protocol 'Self' type.
1946+
void checkNonFinalClassWitness(ValueDecl *requirement,
1947+
ValueDecl *witness);
1948+
19441949
/// Resolve a (non-type) witness via name lookup.
19451950
ResolveWitnessResult resolveWitnessViaLookup(ValueDecl *requirement);
19461951

@@ -3035,6 +3040,115 @@ getAdopteeSelfSameTypeConstraint(ClassDecl *selfClass, ValueDecl *witness) {
30353040
return None;
30363041
}
30373042

3043+
void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
3044+
ValueDecl *witness) {
3045+
auto *classDecl = Adoptee->getClassOrBoundGenericClass();
3046+
3047+
// If we have an initializer requirement and the conforming type
3048+
// is a non-final class, the witness must be 'required'.
3049+
// We exempt Objective-C initializers from this requirement
3050+
// because there is no equivalent to 'required' in Objective-C.
3051+
if (auto ctor = dyn_cast<ConstructorDecl>(witness)) {
3052+
if (!ctor->isRequired() &&
3053+
!ctor->getDeclContext()->getAsProtocolOrProtocolExtensionContext() &&
3054+
!ctor->hasClangNode()) {
3055+
// FIXME: We're not recovering (in the AST), so the Fix-It
3056+
// should move.
3057+
diagnoseOrDefer(requirement, false,
3058+
[ctor, requirement](NormalProtocolConformance *conformance) {
3059+
bool inExtension = isa<ExtensionDecl>(ctor->getDeclContext());
3060+
auto &diags = ctor->getASTContext().Diags;
3061+
auto diag = diags.diagnose(ctor->getLoc(),
3062+
diag::witness_initializer_not_required,
3063+
requirement->getFullName(),
3064+
inExtension,
3065+
conformance->getType());
3066+
if (!ctor->isImplicit() && !inExtension)
3067+
diag.fixItInsert(ctor->getStartLoc(), "required ");
3068+
});
3069+
}
3070+
}
3071+
3072+
// Check whether this requirement uses Self in a way that might
3073+
// prevent conformance from succeeding.
3074+
auto selfKind = Proto->findProtocolSelfReferences(requirement,
3075+
/*allowCovariantParameters=*/false,
3076+
/*skipAssocTypes=*/true);
3077+
3078+
if (selfKind.other) {
3079+
// References to Self in a position where subclasses cannot do
3080+
// the right thing. Complain if the adoptee is a non-final
3081+
// class.
3082+
diagnoseOrDefer(requirement, false,
3083+
[witness, requirement](NormalProtocolConformance *conformance) {
3084+
auto proto = conformance->getProtocol();
3085+
auto &diags = proto->getASTContext().Diags;
3086+
diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype,
3087+
proto->getDeclaredType(), requirement->getFullName(),
3088+
conformance->getType());
3089+
});
3090+
} else if (selfKind.result) {
3091+
// The reference to Self occurs in the result type. A non-final class
3092+
// can satisfy this requirement with a method that returns Self.
3093+
3094+
// If the function has a dynamic Self, it's okay.
3095+
if (auto func = dyn_cast<FuncDecl>(witness)) {
3096+
if (!func->hasDynamicSelf()) {
3097+
diagnoseOrDefer(requirement, false,
3098+
[witness, requirement](NormalProtocolConformance *conformance) {
3099+
auto proto = conformance->getProtocol();
3100+
auto &diags = proto->getASTContext().Diags;
3101+
diags.diagnose(witness->getLoc(),
3102+
diag::witness_requires_dynamic_self,
3103+
requirement->getFullName(),
3104+
conformance->getType(),
3105+
proto->getDeclaredType());
3106+
});
3107+
}
3108+
3109+
// Constructors conceptually also have a dynamic Self
3110+
// return type, so they're okay.
3111+
} else if (!isa<ConstructorDecl>(witness)) {
3112+
diagnoseOrDefer(requirement, false,
3113+
[witness, requirement](NormalProtocolConformance *conformance) {
3114+
auto proto = conformance->getProtocol();
3115+
auto &diags = proto->getASTContext().Diags;
3116+
diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype,
3117+
proto->getDeclaredType(),
3118+
requirement->getFullName(),
3119+
conformance->getType());
3120+
});
3121+
}
3122+
} else if (selfKind.requirement) {
3123+
if (auto constraint = getAdopteeSelfSameTypeConstraint(classDecl,
3124+
witness)) {
3125+
// A "Self ==" constraint works incorrectly with subclasses. Complain.
3126+
auto proto = Conformance->getProtocol();
3127+
auto &diags = proto->getASTContext().Diags;
3128+
diags.diagnose(witness->getLoc(),
3129+
diag::witness_self_same_type,
3130+
witness->getDescriptiveKind(),
3131+
witness->getFullName(),
3132+
Conformance->getType(),
3133+
requirement->getDescriptiveKind(),
3134+
requirement->getFullName(),
3135+
proto->getDeclaredType());
3136+
3137+
if (auto requirementRepr = *constraint) {
3138+
diags.diagnose(requirementRepr->getEqualLoc(),
3139+
diag::witness_self_weaken_same_type,
3140+
requirementRepr->getFirstType(),
3141+
requirementRepr->getSecondType())
3142+
.fixItReplace(requirementRepr->getEqualLoc(), ":");
3143+
}
3144+
}
3145+
}
3146+
3147+
// A non-final class can model a protocol requirement with a
3148+
// contravariant Self, because here the witness will always have
3149+
// a more general type than the requirement.
3150+
}
3151+
30383152
ResolveWitnessResult
30393153
ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
30403154
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
@@ -3245,112 +3359,10 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
32453359
}
32463360
}
32473361

3248-
ClassDecl *classDecl = Adoptee->getClassOrBoundGenericClass();
3249-
3250-
if (classDecl && !classDecl->isFinal()) {
3251-
// If we have an initializer requirement and the conforming type
3252-
// is a non-final class, the witness must be 'required'.
3253-
// We exempt Objective-C initializers from this requirement
3254-
// because there is no equivalent to 'required' in Objective-C.
3255-
if (auto ctor = dyn_cast<ConstructorDecl>(best.Witness)) {
3256-
if (!ctor->isRequired() &&
3257-
!ctor->getDeclContext()->getAsProtocolOrProtocolExtensionContext() &&
3258-
!ctor->hasClangNode()) {
3259-
// FIXME: We're not recovering (in the AST), so the Fix-It
3260-
// should move.
3261-
diagnoseOrDefer(requirement, false,
3262-
[ctor, requirement](NormalProtocolConformance *conformance) {
3263-
bool inExtension = isa<ExtensionDecl>(ctor->getDeclContext());
3264-
auto &diags = ctor->getASTContext().Diags;
3265-
auto diag = diags.diagnose(ctor->getLoc(),
3266-
diag::witness_initializer_not_required,
3267-
requirement->getFullName(),
3268-
inExtension,
3269-
conformance->getType());
3270-
if (!ctor->isImplicit() && !inExtension)
3271-
diag.fixItInsert(ctor->getStartLoc(), "required ");
3272-
});
3273-
}
3274-
}
3275-
3276-
// Check whether this requirement uses Self in a way that might
3277-
// prevent conformance from succeeding.
3278-
auto selfKind = Proto->findProtocolSelfReferences(requirement,
3279-
/*allowCovariantParameters=*/false,
3280-
/*skipAssocTypes=*/true);
3281-
3282-
if (selfKind.other) {
3283-
// References to Self in a position where subclasses cannot do
3284-
// the right thing. Complain if the adoptee is a non-final
3285-
// class.
3286-
diagnoseOrDefer(requirement, false,
3287-
[witness, requirement](NormalProtocolConformance *conformance) {
3288-
auto proto = conformance->getProtocol();
3289-
auto &diags = proto->getASTContext().Diags;
3290-
diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype,
3291-
proto->getDeclaredType(), requirement->getFullName(),
3292-
conformance->getType());
3293-
});
3294-
} else if (selfKind.result) {
3295-
// The reference to Self occurs in the result type. A non-final class
3296-
// can satisfy this requirement with a method that returns Self.
3297-
3298-
// If the function has a dynamic Self, it's okay.
3299-
if (auto func = dyn_cast<FuncDecl>(best.Witness)) {
3300-
if (!func->hasDynamicSelf()) {
3301-
diagnoseOrDefer(requirement, false,
3302-
[witness, requirement](NormalProtocolConformance *conformance) {
3303-
auto proto = conformance->getProtocol();
3304-
auto &diags = proto->getASTContext().Diags;
3305-
diags.diagnose(witness->getLoc(),
3306-
diag::witness_requires_dynamic_self,
3307-
requirement->getFullName(),
3308-
conformance->getType(),
3309-
proto->getDeclaredType());
3310-
});
3311-
}
3312-
3313-
// Constructors conceptually also have a dynamic Self
3314-
// return type, so they're okay.
3315-
} else if (!isa<ConstructorDecl>(best.Witness)) {
3316-
diagnoseOrDefer(requirement, false,
3317-
[witness, requirement](NormalProtocolConformance *conformance) {
3318-
auto proto = conformance->getProtocol();
3319-
auto &diags = proto->getASTContext().Diags;
3320-
diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype,
3321-
proto->getDeclaredType(),
3322-
requirement->getFullName(),
3323-
conformance->getType());
3324-
});
3325-
}
3326-
} else if (selfKind.requirement) {
3327-
if (auto constraint = getAdopteeSelfSameTypeConstraint(classDecl,
3328-
witness)) {
3329-
// A "Self ==" constraint works incorrectly with subclasses. Complain.
3330-
auto proto = Conformance->getProtocol();
3331-
auto &diags = proto->getASTContext().Diags;
3332-
diags.diagnose(witness->getLoc(),
3333-
diag::witness_self_same_type,
3334-
witness->getDescriptiveKind(),
3335-
witness->getFullName(),
3336-
Conformance->getType(),
3337-
requirement->getDescriptiveKind(),
3338-
requirement->getFullName(),
3339-
proto->getDeclaredType());
3340-
3341-
if (auto requirementRepr = *constraint) {
3342-
diags.diagnose(requirementRepr->getEqualLoc(),
3343-
diag::witness_self_weaken_same_type,
3344-
requirementRepr->getFirstType(),
3345-
requirementRepr->getSecondType())
3346-
.fixItReplace(requirementRepr->getEqualLoc(), ":");
3347-
}
3348-
}
3362+
if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) {
3363+
if (!classDecl->isFinal()) {
3364+
checkNonFinalClassWitness(requirement, witness);
33493365
}
3350-
3351-
// A non-final class can model a protocol requirement with a
3352-
// contravariant Self, because here the witness will always have
3353-
// a more general type than the requirement.
33543366
}
33553367

33563368
// Record the match.

0 commit comments

Comments
 (0)