Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

[API Notes] Add support for parameters, 'noescape' attribute <rdar://problem/27256643> #24

Merged
merged 1 commit into from
Jul 11, 2016
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
39 changes: 38 additions & 1 deletion include/clang/APINotes/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ class VariableInfo : public CommonEntityInfo {
Nullable = static_cast<unsigned>(kind);
}


friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) {
return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
lhs.NullabilityAudited == rhs.NullabilityAudited &&
Expand All @@ -279,6 +278,13 @@ class VariableInfo : public CommonEntityInfo {
return !(lhs == rhs);
}

friend VariableInfo &operator|=(VariableInfo &lhs,
const VariableInfo &rhs) {
static_cast<CommonEntityInfo &>(lhs) |= rhs;
if (!lhs.NullabilityAudited && rhs.NullabilityAudited)
lhs.setNullabilityAudited(*rhs.getNullability());
return lhs;
}
};

/// Describes API notes data for an Objective-C property.
Expand All @@ -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<VariableInfo &>(lhs) |= rhs;
if (!lhs.NoEscape && rhs.NoEscape)
lhs.NoEscape = true;
return lhs;
}

friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) {
return static_cast<const VariableInfo &>(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.
///
Expand Down Expand Up @@ -333,6 +367,9 @@ class FunctionInfo : public CommonEntityInfo {
// of the parameters.
uint64_t NullabilityPayload = 0;

/// The function parameters.
std::vector<ParamInfo> Params;

FunctionInfo()
: CommonEntityInfo(),
NullabilityAudited(false),
Expand Down
2 changes: 1 addition & 1 deletion lib/APINotes/APINotesFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
16 changes: 16 additions & 0 deletions lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ namespace {
= endian::readNext<uint8_t, little, unaligned>(data);
info.NullabilityPayload
= endian::readNext<uint64_t, little, unaligned>(data);

unsigned numParams = endian::readNext<uint16_t, little, unaligned>(data);
while (numParams > 0) {
uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data);

ParamInfo pi;
uint8_t nullabilityValue = payload & 0x3; payload >>= 2;
if (payload & 0x01)
pi.setNullabilityAudited(static_cast<NullabilityKind>(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.
Expand Down
17 changes: 16 additions & 1 deletion lib/APINotes/APINotesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -505,6 +506,20 @@ namespace {
writer.write<uint8_t>(info.NullabilityAudited);
writer.write<uint8_t>(info.NumAdjustedNullable);
writer.write<uint64_t>(info.NullabilityPayload);

// Parameters.
writer.write<uint16_t>(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<uint8_t>(*nullability);
writer.write<uint8_t>(payload);
}
}

/// Used to serialize the on-disk Objective-C method table.
Expand Down
55 changes: 55 additions & 0 deletions lib/APINotes/APINotesYAMLCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,17 @@ namespace {
NullabilityKind::NonNull;
typedef std::vector<clang::NullabilityKind> NullabilitySeq;

struct Param {
unsigned Position;
bool NoEscape = false;
llvm::Optional<NullabilityKind> Nullability;
};
typedef std::vector<Param> ParamsSeq;

struct Method {
StringRef Selector;
MethodKind Kind;
ParamsSeq Params;
NullabilitySeq Nullability;
llvm::Optional<NullabilityKind> NullabilityOfRet;
AvailabilityItem Availability;
Expand Down Expand Up @@ -205,6 +213,7 @@ namespace {

struct Function {
StringRef Name;
ParamsSeq Params;
NullabilitySeq Nullability;
llvm::Optional<NullabilityKind> NullabilityOfRet;
AvailabilityItem Availability;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -322,6 +332,16 @@ namespace llvm {
}
};

template <>
struct MappingTraits<Param> {
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<Property> {
static void mapping(IO &io, Property& p) {
Expand All @@ -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);
Expand Down Expand Up @@ -374,6 +395,7 @@ namespace llvm {
struct MappingTraits<Function> {
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);
Expand Down Expand Up @@ -527,6 +549,20 @@ namespace {
return false;
}

void convertParams(const ParamsSeq &params, 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<NullabilityKind> nullabilityOfRet,
FunctionInfo &outInfo,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -924,6 +964,19 @@ namespace {
}
}

/// Map parameter information for a function.
void handleParameters(ParamsSeq &params,
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<NullabilityKind> &nullabilityOfRet,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
46 changes: 30 additions & 16 deletions lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,27 @@ static void ProcessAPINotes(Sema &S, Decl *D,
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(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<NoEscapeAttr>())
D->addAttr(NoEscapeAttr::CreateImplicit(S.Context));

// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(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<const api_notes::VariableInfo &>(Info));
}

/// 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<const api_notes::VariableInfo &>(Info));
}
Expand All @@ -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]);
}
}

Expand Down
7 changes: 7 additions & 0 deletions test/APINotes/Inputs/APINotes/HeaderLib.apinotes
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions test/APINotes/Inputs/Headers/HeaderLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions test/APINotes/Inputs/roundtrip.apinotes
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions test/APINotes/nullability.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down