diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 5d5a3473363..9761510ead2 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -268,7 +268,6 @@ class VariableInfo : public CommonEntityInfo { Nullable = static_cast(kind); } - friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) { return static_cast(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && @@ -279,6 +278,13 @@ class VariableInfo : public CommonEntityInfo { return !(lhs == rhs); } + friend VariableInfo &operator|=(VariableInfo &lhs, + const VariableInfo &rhs) { + static_cast(lhs) |= rhs; + if (!lhs.NullabilityAudited && rhs.NullabilityAudited) + lhs.setNullabilityAudited(*rhs.getNullability()); + return lhs; + } }; /// Describes API notes data for an Objective-C property. @@ -300,6 +306,34 @@ class ObjCPropertyInfo : public VariableInfo { } }; +/// Describes a function or method parameter. +class ParamInfo : public VariableInfo { + /// Whether the this parameter has the 'noescape' attribute. + unsigned NoEscape : 1; + +public: + ParamInfo() : VariableInfo(), NoEscape(false) { } + + bool isNoEscape() const { return NoEscape; } + void setNoEscape(bool noescape) { NoEscape = noescape; } + + friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { + static_cast(lhs) |= rhs; + if (!lhs.NoEscape && rhs.NoEscape) + lhs.NoEscape = true; + return lhs; + } + + friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.NoEscape == rhs.NoEscape; + } + + friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) { + return !(lhs == rhs); + } +}; + /// A temporary reference to an Objective-C selector, suitable for /// referencing selector data on the stack. /// @@ -333,6 +367,9 @@ class FunctionInfo : public CommonEntityInfo { // of the parameters. uint64_t NullabilityPayload = 0; + /// The function parameters. + std::vector Params; + FunctionInfo() : CommonEntityInfo(), NullabilityAudited(false), diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index c999ce16e4b..34db3849626 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 12; // SwiftPrivate +const uint16_t VERSION_MINOR = 13; // Function/method parameters using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 297004e4ddc..bc2f548d1c5 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -228,6 +228,22 @@ namespace { = endian::readNext(data); info.NullabilityPayload = endian::readNext(data); + + unsigned numParams = endian::readNext(data); + while (numParams > 0) { + uint8_t payload = endian::readNext(data); + + ParamInfo pi; + uint8_t nullabilityValue = payload & 0x3; payload >>= 2; + if (payload & 0x01) + pi.setNullabilityAudited(static_cast(nullabilityValue)); + payload >>= 1; + pi.setNoEscape(payload & 0x01); + payload >>= 1; assert(payload == 0 && "Bad API notes"); + + info.Params.push_back(pi); + --numParams; + } } /// Used to deserialize the on-disk Objective-C method table. diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 511efd3dcaa..7178abb00c1 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -494,7 +494,8 @@ namespace { /// Retrieve the serialized size of the given FunctionInfo, for use in /// on-disk hash tables. static unsigned getFunctionInfoSize(const FunctionInfo &info) { - return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info); + return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + + 2 + info.Params.size() * 1; } /// Emit a serialized representation of the function information. @@ -505,6 +506,20 @@ namespace { writer.write(info.NullabilityAudited); writer.write(info.NumAdjustedNullable); writer.write(info.NullabilityPayload); + + // Parameters. + writer.write(info.Params.size()); + for (const auto &pi : info.Params) { + uint8_t payload = pi.isNoEscape(); + + auto nullability = pi.getNullability(); + payload = (payload << 1) | nullability.hasValue(); + + payload = payload << 2; + if (nullability) + payload |= static_cast(*nullability); + writer.write(payload); + } } /// Used to serialize the on-disk Objective-C method table. diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 3e645ba1840..ad2dec64ace 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -166,9 +166,17 @@ namespace { NullabilityKind::NonNull; typedef std::vector NullabilitySeq; + struct Param { + unsigned Position; + bool NoEscape = false; + llvm::Optional Nullability; + }; + typedef std::vector ParamsSeq; + struct Method { StringRef Selector; MethodKind Kind; + ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; @@ -205,6 +213,7 @@ namespace { struct Function { StringRef Name; + ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; @@ -272,6 +281,7 @@ namespace { LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind) LLVM_YAML_IS_SEQUENCE_VECTOR(Method) LLVM_YAML_IS_SEQUENCE_VECTOR(Property) +LLVM_YAML_IS_SEQUENCE_VECTOR(Param) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) @@ -322,6 +332,16 @@ namespace llvm { } }; + template <> + struct MappingTraits { + static void mapping(IO &io, Param& p) { + io.mapRequired("Position", p.Position); + io.mapOptional("Nullability", p.Nullability, + AbsentNullability); + io.mapOptional("NoEscape", p.NoEscape); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Property& p) { @@ -340,6 +360,7 @@ namespace llvm { static void mapping(IO &io, Method& m) { io.mapRequired("Selector", m.Selector); io.mapRequired("MethodKind", m.Kind); + io.mapOptional("Parameters", m.Params); io.mapOptional("Nullability", m.Nullability); io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, AbsentNullability); @@ -374,6 +395,7 @@ namespace llvm { struct MappingTraits { static void mapping(IO &io, Function& f) { io.mapRequired("Name", f.Name); + io.mapOptional("Parameters", f.Params); io.mapOptional("Nullability", f.Nullability); io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, AbsentNullability); @@ -527,6 +549,20 @@ namespace { return false; } + void convertParams(const ParamsSeq ¶ms, FunctionInfo &outInfo) { + for (const auto &p : params) { + ParamInfo pi; + if (p.Nullability) + pi.setNullabilityAudited(*p.Nullability); + pi.setNoEscape(p.NoEscape); + + while (outInfo.Params.size() <= p.Position) { + outInfo.Params.push_back(ParamInfo()); + } + outInfo.Params[p.Position] |= pi; + } + } + void convertNullability(const NullabilitySeq &nullability, Optional nullabilityOfRet, FunctionInfo &outInfo, @@ -610,6 +646,9 @@ namespace { if (meth.FactoryAsInit != FactoryAsInitKind::Infer) mInfo.setFactoryAsInitKind(meth.FactoryAsInit); + // Translate parameter information. + convertParams(meth.Params, mInfo); + // Translate nullability info. convertNullability(meth.Nullability, meth.NullabilityOfRet, mInfo, meth.Selector); @@ -744,6 +783,7 @@ namespace { convertAvailability(function.Availability, info, function.Name); info.SwiftPrivate = function.SwiftPrivate; info.SwiftName = function.SwiftName; + convertParams(function.Params, info); convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); @@ -924,6 +964,19 @@ namespace { } } + /// Map parameter information for a function. + void handleParameters(ParamsSeq ¶ms, + const FunctionInfo &info) { + unsigned position = 0; + for (const auto &pi: info.Params) { + Param p; + p.Position = position++; + p.Nullability = pi.getNullability(); + p.NoEscape = pi.isNoEscape(); + params.push_back(p); + } + } + /// Map nullability information for a function. void handleNullability(NullabilitySeq &nullability, llvm::Optional &nullabilityOfRet, @@ -967,6 +1020,7 @@ namespace { method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; handleCommon(method, info); + handleParameters(method.Params, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); method.FactoryAsInit = info.getFactoryAsInitKind(); @@ -1003,6 +1057,7 @@ namespace { Function function; function.Name = name; handleCommon(function, info); + handleParameters(function.Params, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 0515be234e4..445a52c8ed1 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -175,10 +175,20 @@ static void ProcessAPINotes(Sema &S, Decl *D, ProcessAPINotes(S, D, static_cast(Info)); } +/// Process API notes for a parameter. +static void ProcessAPINotes(Sema &S, ParmVarDecl *D, + const api_notes::ParamInfo &Info) { + // noescape + if (Info.isNoEscape() && !D->getAttr()) + D->addAttr(NoEscapeAttr::CreateImplicit(S.Context)); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(Info)); +} + /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, const api_notes::GlobalVariableInfo &Info) { - // Handle common entity information. ProcessAPINotes(S, D, static_cast(Info)); } @@ -186,7 +196,6 @@ static void ProcessAPINotes(Sema &S, VarDecl *D, /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, const api_notes::ObjCPropertyInfo &Info) { - // Handle common entity information. ProcessAPINotes(S, D, static_cast(Info)); } @@ -207,26 +216,31 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, D = MD; } - // Nullability. + // Nullability of return type. if (Info.NullabilityAudited) { - // Return type. applyNullability(S, D, Info.getReturnTypeInfo()); + } - // Parameters. - unsigned NumParams; + // Parameters. + unsigned NumParams; + if (FD) + NumParams = FD->getNumParams(); + else + NumParams = MD->param_size(); + + for (unsigned I = 0; I != NumParams; ++I) { + ParmVarDecl *Param; if (FD) - NumParams = FD->getNumParams(); + Param = FD->getParamDecl(I); else - NumParams = MD->param_size(); - - for (unsigned I = 0; I != NumParams; ++I) { - ParmVarDecl *Param; - if (FD) - Param = FD->getParamDecl(I); - else - Param = MD->param_begin()[I]; - + Param = MD->param_begin()[I]; + + // Nullability. + if (Info.NullabilityAudited) applyNullability(S, Param, Info.getParamTypeInfo(I)); + + if (I < Info.Params.size()) { + ProcessAPINotes(S, Param, Info.Params[I]); } } diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes index 8d8ff11f69b..9df8c3d5e46 100644 --- a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -9,6 +9,13 @@ Functions: - Name: do_something_with_pointers NullabilityOfRet: O Nullability: [ N, O ] + - Name: take_pointer_and_int + Parameters: + - Position: 0 + Nullability: N + NoEscape: true + - Position: 1 + NoEscape: true Globals: - Name: global_int diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h index 81a7d63d468..ec66166adb2 100644 --- a/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/test/APINotes/Inputs/Headers/HeaderLib.h @@ -13,4 +13,6 @@ void do_something_with_pointers(int *ptr1, int *ptr2); typedef int unavailable_typedef; struct unavailable_struct { int x, y, z; }; +void take_pointer_and_int(int *ptr1, int value); + #endif diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 66f5ea7f8f2..09027347901 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -67,6 +67,13 @@ Classes: SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance + Parameters: + - Position: 0 + NoEscape: false + - Position: 1 + NoEscape: false + - Position: 2 + NoEscape: true Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c index 86054eade82..1d5939bf92e 100644 --- a/test/APINotes/nullability.c +++ b/test/APINotes/nullability.c @@ -8,6 +8,7 @@ int main() { int i = 0; do_something_with_pointers(&i, 0); do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}} + take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}} float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}} return 0;