diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index ce1ce54d1560..9eb6d81296d8 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2011,10 +2011,11 @@ public: Optional getNullability(const ASTContext &context) const; /// Determine whether the given type can have a nullability - /// specifier applied to it, i.e., if it is any kind of pointer type - /// or a dependent type that could instantiate to any kind of - /// pointer type. - bool canHaveNullability() const; + /// specifier applied to it, i.e., if it is any kind of pointer type. + /// + /// \param ResultIfUnknown The value to return if we don't yet know whether + /// this type can have nullability because it is dependent. + bool canHaveNullability(bool ResultIfUnknown = true) const; /// Retrieve the set of substitutions required when accessing a member /// of the Objective-C receiver type that is declared in the given context. diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 22d52bcd3f31..1e10094aeeea 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3531,7 +3531,7 @@ Optional Type::getNullability(const ASTContext &context) const } while (true); } -bool Type::canHaveNullability() const { +bool Type::canHaveNullability(bool ResultIfUnknown) const { QualType type = getCanonicalTypeInternal(); switch (type->getTypeClass()) { @@ -3559,7 +3559,8 @@ bool Type::canHaveNullability() const { case Type::SubstTemplateTypeParmPack: case Type::DependentName: case Type::DependentTemplateSpecialization: - return true; + case Type::Auto: + return ResultIfUnknown; // Dependent template specializations can instantiate to pointer // types unless they're known to be specializations of a class @@ -3571,12 +3572,7 @@ bool Type::canHaveNullability() const { if (isa(templateDecl)) return false; } - return true; - - // auto is considered dependent when it isn't deduced. - case Type::Auto: - case Type::DeducedTemplateSpecialization: - return !cast(type.getTypePtr())->isDeduced(); + return ResultIfUnknown; case Type::Builtin: switch (cast(type.getTypePtr())->getKind()) { @@ -3595,7 +3591,7 @@ bool Type::canHaveNullability() const { case BuiltinType::PseudoObject: case BuiltinType::UnknownAny: case BuiltinType::ARCUnbridgedCast: - return true; + return ResultIfUnknown; case BuiltinType::Void: case BuiltinType::ObjCId: @@ -3614,6 +3610,7 @@ bool Type::canHaveNullability() const { case BuiltinType::OMPArraySection: return false; } + llvm_unreachable("unknown builtin type"); // Non-pointer types. case Type::Complex: @@ -3629,6 +3626,7 @@ bool Type::canHaveNullability() const { case Type::FunctionProto: case Type::FunctionNoProto: case Type::Record: + case Type::DeducedTemplateSpecialization: case Type::Enum: case Type::InjectedClassName: case Type::PackExpansion: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 1433607e9c71..f8970744389b 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3735,16 +3735,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // inner pointers. complainAboutMissingNullability = CAMN_InnerPointers; - auto isDependentNonPointerType = [](QualType T) -> bool { - // Note: This is intended to be the same check as Type::canHaveNullability - // except with all of the ambiguous cases being treated as 'false' rather - // than 'true'. - return T->isDependentType() && !T->isAnyPointerType() && - !T->isBlockPointerType() && !T->isMemberPointerType(); - }; - - if (T->canHaveNullability() && !T->getNullability(S.Context) && - !isDependentNonPointerType(T)) { + if (T->canHaveNullability(/*ResultIfUnknown*/false) && + !T->getNullability(S.Context)) { // Note that we allow but don't require nullability on dependent types. ++NumPointersRemaining; } @@ -3962,7 +3954,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // If the type itself could have nullability but does not, infer pointer // nullability and perform consistency checking. if (S.CodeSynthesisContexts.empty()) { - if (T->canHaveNullability() && !T->getNullability(S.Context)) { + if (T->canHaveNullability(/*ResultIfUnknown*/false) && + !T->getNullability(S.Context)) { if (isVaList(T)) { // Record that we've seen a pointer, but do nothing else. if (NumPointersRemaining > 0) diff --git a/clang/test/SemaCXX/Inputs/nullability-completeness.h b/clang/test/SemaCXX/Inputs/nullability-completeness.h new file mode 100644 index 000000000000..1b30e1b4d906 --- /dev/null +++ b/clang/test/SemaCXX/Inputs/nullability-completeness.h @@ -0,0 +1,4 @@ +template struct Template final { + int *_Nonnull x; + T y; +}; diff --git a/clang/test/SemaCXX/nullability.cpp b/clang/test/SemaCXX/nullability.cpp index 160741b1409c..99375a9fdde3 100644 --- a/clang/test/SemaCXX/nullability.cpp +++ b/clang/test/SemaCXX/nullability.cpp @@ -1,10 +1,12 @@ -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify -Wnullable-to-nonnull-conversion +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify -Wnullable-to-nonnull-conversion -I%S/Inputs #if __has_feature(nullability) #else # error nullability feature should be defined #endif +#include "nullability-completeness.h" + typedef decltype(nullptr) nullptr_t; class X { @@ -130,3 +132,7 @@ void arraysInLambdas() { withTypedef(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}} auto withTypedefBad = [](INTS _Nonnull[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}} } + +void testNullabilityCompletenessWithTemplate() { + Template tip; +}