Merge pull request #42 from DougGregor/api-notes-change-types-3_1
[API Notes] Add support for overriding the types of declarations via API notes.
diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h
index 09fbc8d..6fbd5d8 100644
--- a/include/clang/APINotes/Types.h
+++ b/include/clang/APINotes/Types.h
@@ -297,6 +297,9 @@
/// has been audited.
unsigned Nullable : 2;
+ /// The C type of the variable, as a string.
+ std::string Type;
+
public:
VariableInfo()
: CommonEntityInfo(),
@@ -315,10 +318,14 @@
Nullable = static_cast<unsigned>(kind);
}
+ const std::string &getType() const { return Type; }
+ void setType(const std::string &type) { Type = type; }
+
friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) {
return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
lhs.NullabilityAudited == rhs.NullabilityAudited &&
- lhs.Nullable == rhs.Nullable;
+ lhs.Nullable == rhs.Nullable &&
+ lhs.Type == rhs.Type;
}
friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) {
@@ -330,6 +337,8 @@
static_cast<CommonEntityInfo &>(lhs) |= rhs;
if (!lhs.NullabilityAudited && rhs.NullabilityAudited)
lhs.setNullabilityAudited(*rhs.getNullability());
+ if (lhs.Type.empty() && !rhs.Type.empty())
+ lhs.Type = rhs.Type;
return lhs;
}
};
@@ -347,6 +356,8 @@
/// Merge class-wide information into the given property.
friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs,
const ObjCContextInfo &rhs) {
+ static_cast<VariableInfo &>(lhs) |= rhs;
+
// Merge nullability.
if (!lhs.getNullability()) {
if (auto nullable = rhs.getDefaultNullability()) {
@@ -462,6 +473,9 @@
// of the parameters.
uint64_t NullabilityPayload = 0;
+ /// The result type of this function, as a C type.
+ std::string ResultType;
+
/// The function parameters.
std::vector<ParamInfo> Params;
@@ -525,7 +539,9 @@
return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
lhs.NullabilityAudited == rhs.NullabilityAudited &&
lhs.NumAdjustedNullable == rhs.NumAdjustedNullable &&
- lhs.NullabilityPayload == rhs.NullabilityPayload;
+ lhs.NullabilityPayload == rhs.NullabilityPayload &&
+ lhs.ResultType == rhs.ResultType &&
+ lhs.Params == rhs.Params;
}
friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) {
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index fb3db57..6f55fec 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -1014,6 +1014,9 @@
def err_pragma_invalid_keyword : Error<
"invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
+// API notes.
+def err_type_unparsed : Error<"unparsed tokens following type">;
+
// Pragma unroll support.
def warn_pragma_unroll_cuda_value_in_parens : Warning<
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index f9efa41..cc5e9fb 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8157,6 +8157,13 @@
"blocks in this form of device side enqueue call are expected to have have no parameters">;
} // end of sema category
+let CategoryName = "API Notes Issue" in {
+
+def err_incompatible_replacement_type : Error<
+ "API notes replacement type %0 has a different size from original type %1">;
+
+}
+
let CategoryName = "OpenMP Issue" in {
// OpenMP support.
def err_omp_expected_var_arg : Error<
diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 830c25a..6f1f7c6 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -133,15 +133,17 @@
/// from. Currently this is only used by _Pragma handling.
SourceLocation getFileLoc() const { return FileLoc; }
-private:
/// Lex - Return the next token in the file. If this is the end of file, it
/// return the tok::eof token. This implicitly involves the preprocessor.
bool Lex(Token &Result);
-public:
/// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma.
bool isPragmaLexer() const { return Is_PragmaLexer; }
+ /// Note that this Lexer is being used to lex a pragma, or something like it
+ /// that has simple end-of-file behavior.
+ void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; }
+
private:
/// IndirectLex - An indirect call to 'Lex' that can be invoked via
/// the PreprocessorLexer interface.
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 8117741..f1157c3 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -2681,7 +2681,19 @@
//===--------------------------------------------------------------------===//
// C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
ExprResult ParseTypeTrait();
-
+
+ /// Parse the given string as a type.
+ ///
+ /// This is a dangerous utility function currently employed only by API notes.
+ /// It is not a general entry-point for safely parsing types from strings.
+ ///
+ /// \param typeStr The string to be parsed as a type.
+ /// \param context The name of the context in which this string is being
+ /// parsed, which will be used in diagnostics.
+ /// \param includeLoc The location at which this parse was triggered.
+ TypeResult parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc);
+
//===--------------------------------------------------------------------===//
// Embarcadero: Arary and Expression Traits
ExprResult ParseArrayTypeTrait();
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 4080a54..87dffa6 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -54,6 +54,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TinyPtrVector.h"
#include <deque>
+#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -572,6 +573,10 @@
OpaqueParser = P;
}
+ /// \brief Callback to the parser to parse a type expressed as a string.
+ std::function<TypeResult(StringRef, StringRef, SourceLocation)>
+ ParseTypeFromStringCallback;
+
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@@ -1778,6 +1783,8 @@
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
+ QualType adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc);
ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc,
SourceLocation NameLoc, IdentifierInfo *Name,
QualType T, TypeSourceInfo *TSInfo,
diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h
index b3d2d36..ef3ef73 100644
--- a/lib/APINotes/APINotesFormat.h
+++ b/lib/APINotes/APINotesFormat.h
@@ -36,7 +36,7 @@
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 20; // ImportPropertyAsAccessors
+const uint16_t VERSION_MINOR = 21; // Override types
using IdentifierID = PointerEmbeddedInt<unsigned, 31>;
using IdentifierIDField = BCVBR<16>;
diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp
index 1daf27b..e889ac4 100644
--- a/lib/APINotes/APINotesReader.cpp
+++ b/lib/APINotes/APINotesReader.cpp
@@ -265,6 +265,11 @@
info.setNullabilityAudited(static_cast<NullabilityKind>(*data));
}
++data;
+
+ auto typeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.setType(std::string(data, data + typeLen));
+ data += typeLen;
}
/// Used to deserialize the on-disk Objective-C property table.
@@ -292,6 +297,17 @@
}
};
+ /// Read serialized ParamInfo.
+ void readParamInfo(const uint8_t *&data, ParamInfo &info) {
+ readVariableInfo(data, info);
+
+ uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data);
+ if (payload & 0x01) {
+ info.setNoEscape(payload & 0x02);
+ }
+ payload >>= 2; assert(payload == 0 && "Bad API notes");
+ }
+
/// Read serialized FunctionInfo.
void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) {
readCommonEntityInfo(data, info);
@@ -304,21 +320,16 @@
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;
- if (payload & 0x01) {
- pi.setNoEscape(payload & 0x02);
- }
- payload >>= 2; assert(payload == 0 && "Bad API notes");
-
+ readParamInfo(data, pi);
info.Params.push_back(pi);
--numParams;
}
+
+ unsigned resultTypeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.ResultType = std::string(data, data + resultTypeLen);
+ data += resultTypeLen;
}
/// Used to deserialize the on-disk Objective-C method table.
diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp
index d696aa6..86b6abd 100644
--- a/lib/APINotes/APINotesWriter.cpp
+++ b/lib/APINotes/APINotesWriter.cpp
@@ -490,7 +490,7 @@
/// Retrieve the serialized size of the given VariableInfo, for use in
/// on-disk hash tables.
unsigned getVariableInfoSize(const VariableInfo &info) {
- return 2 + getCommonEntityInfoSize(info);
+ return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size();
}
/// Emit a serialized representation of the variable information.
@@ -506,6 +506,10 @@
}
out.write(reinterpret_cast<const char *>(bytes), 2);
+
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(info.getType().size());
+ out.write(info.getType().data(), info.getType().size());
}
/// On-dish hash table info key base for handling versioned data.
@@ -691,11 +695,34 @@
}
namespace {
+ static unsigned getParamInfoSize(const ParamInfo &info) {
+ return getVariableInfoSize(info) + 1;
+ }
+
+ static void emitParamInfo(raw_ostream &out, const ParamInfo &info) {
+ emitVariableInfo(out, info);
+
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto noescape = info.isNoEscape()) {
+ payload |= 0x01;
+ if (*noescape)
+ payload |= 0x02;
+ }
+ writer.write<uint8_t>(payload);
+ }
+
/// 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) +
- 2 + info.Params.size() * 1;
+ unsigned size = 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + 2;
+
+ for (const auto ¶m : info.Params)
+ size += getParamInfoSize(param);
+
+ size += 2 + info.ResultType.size();
+ return size;
}
/// Emit a serialized representation of the function information.
@@ -709,22 +736,12 @@
// Parameters.
writer.write<uint16_t>(info.Params.size());
- for (const auto &pi : info.Params) {
- uint8_t payload = 0;
- if (auto noescape = pi.isNoEscape()) {
- payload |= 0x01;
- if (*noescape)
- payload |= 0x02;
- }
+ for (const auto &pi : info.Params)
+ emitParamInfo(out, pi);
- 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);
- }
+ // Result type.
+ writer.write<uint16_t>(info.ResultType.size());
+ out.write(info.ResultType.data(), info.ResultType.size());
}
/// Used to serialize the on-disk Objective-C method table.
diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp
index 1f3783c..0ac0b6d 100644
--- a/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -173,6 +173,7 @@
unsigned Position;
Optional<bool> NoEscape = false;
llvm::Optional<NullabilityKind> Nullability;
+ StringRef Type;
};
typedef std::vector<Param> ParamsSeq;
@@ -189,6 +190,7 @@
= api_notes::FactoryAsInitKind::Infer;
bool DesignatedInit = false;
bool Required = false;
+ StringRef ResultType;
};
typedef std::vector<Method> MethodsSeq;
@@ -200,6 +202,7 @@
Optional<bool> SwiftPrivate;
StringRef SwiftName;
Optional<bool> SwiftImportAsAccessors;
+ StringRef Type;
};
typedef std::vector<Property> PropertiesSeq;
@@ -224,6 +227,8 @@
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
+ StringRef Type;
+ StringRef ResultType;
};
typedef std::vector<Function> FunctionsSeq;
@@ -233,6 +238,7 @@
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
+ StringRef Type;
};
typedef std::vector<GlobalVariable> GlobalVariablesSeq;
@@ -385,6 +391,7 @@
io.mapOptional("Nullability", p.Nullability,
AbsentNullability);
io.mapOptional("NoEscape", p.NoEscape);
+ io.mapOptional("Type", p.Type, StringRef(""));
}
};
@@ -400,6 +407,7 @@
io.mapOptional("SwiftPrivate", p.SwiftPrivate);
io.mapOptional("SwiftName", p.SwiftName);
io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors);
+ io.mapOptional("Type", p.Type, StringRef(""));
}
};
@@ -420,6 +428,7 @@
api_notes::FactoryAsInitKind::Infer);
io.mapOptional("DesignatedInit", m.DesignatedInit, false);
io.mapOptional("Required", m.Required, false);
+ io.mapOptional("ResultType", m.ResultType, StringRef(""));
}
};
@@ -451,6 +460,7 @@
io.mapOptional("AvailabilityMsg", f.Availability.Msg);
io.mapOptional("SwiftPrivate", f.SwiftPrivate);
io.mapOptional("SwiftName", f.SwiftName);
+ io.mapOptional("ResultType", f.ResultType, StringRef(""));
}
};
@@ -464,6 +474,7 @@
io.mapOptional("AvailabilityMsg", v.Availability.Msg);
io.mapOptional("SwiftPrivate", v.SwiftPrivate);
io.mapOptional("SwiftName", v.SwiftName);
+ io.mapOptional("Type", v.Type, StringRef(""));
}
};
@@ -621,7 +632,7 @@
if (p.Nullability)
pi.setNullabilityAudited(*p.Nullability);
pi.setNoEscape(p.NoEscape);
-
+ pi.setType(p.Type);
while (outInfo.Params.size() <= p.Position) {
outInfo.Params.push_back(ParamInfo());
}
@@ -712,6 +723,7 @@
mInfo.Required = meth.Required;
if (meth.FactoryAsInit != FactoryAsInitKind::Infer)
mInfo.setFactoryAsInitKind(meth.FactoryAsInit);
+ mInfo.ResultType = meth.ResultType;
// Translate parameter information.
convertParams(meth.Params, mInfo);
@@ -788,6 +800,7 @@
pInfo.setNullabilityAudited(*prop.Nullability);
if (prop.SwiftImportAsAccessors)
pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors);
+ pInfo.setType(prop.Type);
if (prop.Kind) {
Writer->addObjCProperty(clID, prop.Name,
*prop.Kind == MethodKind::Instance, pInfo,
@@ -844,6 +857,7 @@
info.SwiftName = global.SwiftName;
if (global.Nullability)
info.setNullabilityAudited(*global.Nullability);
+ info.setType(global.Type);
Writer->addGlobalVariable(global.Name, info, swiftVersion);
}
@@ -867,7 +881,7 @@
convertNullability(function.Nullability,
function.NullabilityOfRet,
info, function.Name);
-
+ info.ResultType = function.ResultType;
Writer->addGlobalFunction(function.Name, info, swiftVersion);
}
@@ -1112,6 +1126,7 @@
p.Position = position++;
p.Nullability = pi.getNullability();
p.NoEscape = pi.isNoEscape();
+ p.Type = copyString(pi.getType());
params.push_back(p);
}
}
@@ -1181,7 +1196,7 @@
method.FactoryAsInit = info.getFactoryAsInitKind();
method.DesignatedInit = info.DesignatedInit;
method.Required = info.Required;
-
+ method.ResultType = copyString(info.ResultType);
auto &items = getTopLevelItems(swiftVersion);
knownContexts[contextID.Value].getContext(swiftVersion, items)
.Methods.push_back(method);
@@ -1203,6 +1218,8 @@
property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors();
+ property.Type = copyString(info.getType());
+
auto &items = getTopLevelItems(swiftVersion);
knownContexts[contextID.Value].getContext(swiftVersion, items)
.Properties.push_back(property);
@@ -1218,7 +1235,7 @@
if (info.NumAdjustedNullable > 0)
handleNullability(function.Nullability, function.NullabilityOfRet,
info, info.NumAdjustedNullable-1);
-
+ function.ResultType = copyString(info.ResultType);
auto &items = getTopLevelItems(swiftVersion);
items.Functions.push_back(function);
}
@@ -1234,6 +1251,7 @@
if (auto nullability = info.getNullability()) {
global.Nullability = *nullability;
}
+ global.Type = copyString(info.getType());
auto &items = getTopLevelItems(swiftVersion);
items.Globals.push_back(global);
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index ebf30a7..82195de 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -6558,3 +6558,68 @@
}
return false;
}
+
+TypeResult Parser::parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc) {
+ // Consume (unexpanded) tokens up to the end-of-directive.
+ SmallVector<Token, 4> tokens;
+ {
+ // Create a new buffer from which we will parse the type.
+ auto &sourceMgr = PP.getSourceManager();
+ FileID fileID = sourceMgr.createFileID(
+ llvm::MemoryBuffer::getMemBufferCopy(typeStr, context),
+ SrcMgr::C_User, 0, 0, includeLoc);
+
+ // Form a new lexer that references the buffer.
+ Lexer lexer(fileID, sourceMgr.getBuffer(fileID), PP);
+ lexer.setParsingPreprocessorDirective(true);
+ lexer.setIsPragmaLexer(true);
+
+ // Lex the tokens from that buffer.
+ Token tok;
+ do {
+ lexer.Lex(tok);
+ tokens.push_back(tok);
+ } while (tok.isNot(tok::eod));
+ }
+
+ // Replace the "eod" token with an "eof" token identifying the end of
+ // the provided string.
+ Token &endToken = tokens.back();
+ endToken.startToken();
+ endToken.setKind(tok::eof);
+ endToken.setLocation(Tok.getLocation());
+ endToken.setEofData(typeStr.data());
+
+ // Add the current token back.
+ tokens.push_back(Tok);
+
+ // Enter the tokens into the token stream.
+ PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false);
+
+ // Consume the current token so that we'll start parsing the tokens we
+ // added to the stream.
+ ConsumeAnyToken();
+
+ // Enter a new scope.
+ ParseScope localScope(this, 0);
+
+ // Parse the type.
+ TypeResult result = ParseTypeName(nullptr);
+
+ // Check if we parsed the whole thing.
+ if (result.isUsable() &&
+ (Tok.isNot(tok::eof) || Tok.getEofData() != typeStr.data())) {
+ Diag(Tok.getLocation(), diag::err_type_unparsed);
+ }
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the 'end of directive' token.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the end token.
+ if (Tok.is(tok::eof) && Tok.getEofData() == typeStr.data())
+ ConsumeAnyToken();
+ return result;
+}
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 2d35ad6..8be75c2 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -88,6 +88,11 @@
PP.addCommentHandler(CommentSemaHandler.get());
PP.setCodeCompletionHandler(*this);
+
+ Actions.ParseTypeFromStringCallback =
+ [this](StringRef typeStr, StringRef context, SourceLocation includeLoc) {
+ return this->parseTypeFromString(typeStr, context, includeLoc);
+ };
}
DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
@@ -420,6 +425,9 @@
//===----------------------------------------------------------------------===//
Parser::~Parser() {
+ // Clear out the parse-type-from-string callback.
+ Actions.ParseTypeFromStringCallback = nullptr;
+
// If we still have scopes active, delete the scope tree.
delete getCurScope();
Actions.CurScope = nullptr;
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
index 8f09313..064bbe8 100644
--- a/lib/Sema/SemaAPINotes.cpp
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -264,10 +264,62 @@
role);
}
+/// Check that the replacement type provided by API notes is reasonable.
+///
+/// This is a very weak form of ABI check.
+static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc,
+ QualType origType,
+ QualType replacementType) {
+ if (S.Context.getTypeSize(origType) !=
+ S.Context.getTypeSize(replacementType)) {
+ S.Diag(loc, diag::err_incompatible_replacement_type)
+ << replacementType << origType;
+ return true;
+ }
+
+ return false;
+}
+
/// Process API notes for a variable or property.
static void ProcessAPINotes(Sema &S, Decl *D,
const api_notes::VariableInfo &info,
VersionedInfoRole role) {
+ // Type override.
+ if (role != VersionedInfoRole::Versioned &&
+ !info.getType().empty() && S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.getType(),
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType type = Sema::GetTypeFromParser(parsedType.get());
+ auto typeInfo =
+ S.Context.getTrivialTypeSourceInfo(type, D->getLocation());
+
+ if (auto var = dyn_cast<VarDecl>(D)) {
+ // Make adjustments to parameter types.
+ if (isa<ParmVarDecl>(var)) {
+ type = S.adjustParameterTypeForObjCAutoRefCount(type,
+ D->getLocation());
+ type = S.Context.getAdjustedParameterType(type);
+ }
+
+ if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(),
+ type)) {
+ var->setType(type);
+ var->setTypeSourceInfo(typeInfo);
+ }
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
+ if (!checkAPINotesReplacementType(S, property->getLocation(),
+ property->getType(),
+ type)) {
+ property->setType(type, typeInfo);
+ }
+ } else {
+ llvm_unreachable("API notes allowed a type on an unknown declaration");
+ }
+ }
+ }
+
// Nullability.
if (auto Nullability = info.getNullability()) {
applyNullability(S, D, *Nullability, role);
@@ -347,20 +399,75 @@
NumParams = FD->getNumParams();
else
NumParams = MD->param_size();
-
+
+ bool anyTypeChanged = false;
for (unsigned I = 0; I != NumParams; ++I) {
ParmVarDecl *Param;
if (FD)
Param = FD->getParamDecl(I);
else
Param = MD->param_begin()[I];
-
+
+ QualType paramTypeBefore = Param->getType();
+
+ if (I < info.Params.size()) {
+ ProcessAPINotes(S, Param, info.Params[I], role);
+ }
+
// Nullability.
if (info.NullabilityAudited)
applyNullability(S, Param, info.getParamTypeInfo(I), role);
- if (I < info.Params.size()) {
- ProcessAPINotes(S, Param, info.Params[I], role);
+ if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
+ anyTypeChanged = true;
+ }
+
+ // Result type override.
+ QualType overriddenResultType;
+ if (role != VersionedInfoRole::Versioned && !info.ResultType.empty() &&
+ S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.ResultType,
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType resultType = Sema::GetTypeFromParser(parsedType.get());
+
+ if (MD) {
+ if (!checkAPINotesReplacementType(S, D->getLocation(),
+ MD->getReturnType(), resultType)) {
+ auto resultTypeInfo =
+ S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation());
+ MD->setReturnType(resultType);
+ MD->setReturnTypeSourceInfo(resultTypeInfo);
+ }
+ } else if (!checkAPINotesReplacementType(S, FD->getLocation(),
+ FD->getReturnType(),
+ resultType)) {
+ overriddenResultType = resultType;
+ anyTypeChanged = true;
+ }
+ }
+ }
+
+ // If the result type or any of the parameter types changed for a function
+ // declaration, we have to rebuild the type.
+ if (FD && anyTypeChanged) {
+ if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
+ if (overriddenResultType.isNull())
+ overriddenResultType = fnProtoType->getReturnType();
+
+ SmallVector<QualType, 4> paramTypes;
+ for (auto param : FD->parameters()) {
+ paramTypes.push_back(param->getType());
+ }
+ FD->setType(S.Context.getFunctionType(overriddenResultType,
+ paramTypes,
+ fnProtoType->getExtProtoInfo()));
+ } else if (!overriddenResultType.isNull()) {
+ const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
+ FD->setType(
+ S.Context.getFunctionNoProtoType(overriddenResultType,
+ fnNoProtoType->getExtInfo()));
}
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index e10c545..94a455a 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -11121,10 +11121,8 @@
}
}
-ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
- SourceLocation NameLoc, IdentifierInfo *Name,
- QualType T, TypeSourceInfo *TSInfo,
- StorageClass SC) {
+QualType Sema::adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc) {
// In ARC, infer a lifetime qualifier for appropriate parameter types.
if (getLangOpts().ObjCAutoRefCount &&
T.getObjCLifetime() == Qualifiers::OCL_None &&
@@ -11139,7 +11137,7 @@
if (!T.isConstQualified()) {
DelayedDiagnostics.add(
sema::DelayedDiagnostic::makeForbiddenType(
- NameLoc, diag::err_arc_array_param_no_ownership, T, false));
+ Loc, diag::err_arc_array_param_no_ownership, T, false));
}
lifetime = Qualifiers::OCL_ExplicitNone;
} else {
@@ -11148,6 +11146,16 @@
T = Context.getLifetimeQualifiedType(T, lifetime);
}
+ return T;
+}
+
+ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
+ SourceLocation NameLoc, IdentifierInfo *Name,
+ QualType T, TypeSourceInfo *TSInfo,
+ StorageClass SC) {
+ // Perform Objective-C ARC adjustments.
+ T = adjustParameterTypeForObjCAutoRefCount(T, NameLoc);
+
ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
Context.getAdjustedParameterType(T),
TSInfo, SC, nullptr);
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
index aa43d84..817af12 100644
--- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
@@ -31,6 +31,31 @@
- Selector: "initWithA:"
MethodKind: Instance
DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double *'
SwiftVersions:
- Version: 3.0
Classes:
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
index aa43d84..d2d1003 100644
--- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
@@ -31,6 +31,31 @@
- Selector: "initWithA:"
MethodKind: Instance
DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double (*)(int, int)'
SwiftVersions:
- Version: 3.0
Classes:
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
index 60d9afa..42a9fbc 100644
--- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
@@ -37,4 +37,16 @@
#import <SomeKit/SomeKitExplicitNullability.h>
+extern int *global_int_ptr;
+
+int *global_int_fun(int *ptr, int *ptr2);
+
+#define SOMEKIT_DOUBLE double
+
+__attribute__((objc_root_class))
+@interface OverriddenTypes
+-(int *)methodToMangle:(int *)ptr1 second:(int *)ptr2;
+@property int *intPropertyToMangle;
+@end
+
#endif
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.apinotes b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
new file mode 100644
index 0000000..00f7b50
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
@@ -0,0 +1,10 @@
+Name: BrokenTypes
+Functions:
+ - Name: break_me_function
+ ResultType: 'int * with extra junk'
+ Parameters:
+ - Position: 0
+ Type: 'not_a_type'
+Globals:
+ - Name: break_me_variable
+ Type: 'double'
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.h b/test/APINotes/Inputs/Headers/BrokenTypes.h
new file mode 100644
index 0000000..fee054b
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.h
@@ -0,0 +1,8 @@
+#ifndef BROKEN_TYPES_H
+#define BROKEN_TYPES_H
+
+char break_me_function(void *ptr);
+
+extern char break_me_variable;
+
+#endif // BROKEN_TYPES_H
diff --git a/test/APINotes/Inputs/Headers/module.modulemap b/test/APINotes/Inputs/Headers/module.modulemap
index 3e59efc..54af0f5 100644
--- a/test/APINotes/Inputs/Headers/module.modulemap
+++ b/test/APINotes/Inputs/Headers/module.modulemap
@@ -1,3 +1,7 @@
module HeaderLib {
header "HeaderLib.h"
}
+
+module BrokenTypes {
+ header "BrokenTypes.h"
+}
diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes
index 68169c4..05599a7 100644
--- a/test/APINotes/Inputs/roundtrip.apinotes
+++ b/test/APINotes/Inputs/roundtrip.apinotes
@@ -17,6 +17,7 @@
SwiftPrivate: false
SwiftName: ''
FactoryAsInit: C
+ ResultType: id
- Selector: init
MethodKind: Instance
NullabilityOfRet: U
@@ -77,6 +78,7 @@
- Position: 1
- Position: 2
NoEscape: true
+ Type: id
Nullability: [ N, N, O ]
NullabilityOfRet: N
Availability: available
@@ -97,6 +99,7 @@
Availability: available
AvailabilityMsg: ''
SwiftName: enclosing
+ Type: id
- Name: makeBackingLayer
PropertyKind: Class
Nullability: N
@@ -111,6 +114,7 @@
Availability: available
AvailabilityMsg: ''
SwiftName: 'availableWindowDepths()'
+ ResultType: NSInteger
Globals:
- Name: NSCalibratedWhiteColorSpace
Nullability: N
@@ -118,6 +122,7 @@
AvailabilityMsg: ''
SwiftPrivate: false
SwiftName: calibratedWhite
+ Type: id
Enumerators:
- Name: NSColorRed
Availability: available
diff --git a/test/APINotes/broken_types.m b/test/APINotes/broken_types.m
new file mode 100644
index 0000000..164ae79
--- /dev/null
+++ b/test/APINotes/broken_types.m
@@ -0,0 +1,19 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err
+// RUN: FileCheck %s < %t.err
+
+#include "BrokenTypes.h"
+
+// CHECK: <API Notes>:1:1: error: unknown type name 'not_a_type'
+// CHECK-NEXT: not_a_type
+// CHECK-NEXT: ^
+
+// CHECK: <API Notes>:1:7: error: unparsed tokens following type
+// CHECK-NEXT: int * with extra junk
+// CHECK-NEXT: ^
+
+// CHECK: BrokenTypes.h:4:6: error: API notes replacement type 'int *' has a different size from original type 'char'
+
+// CHECK: BrokenTypes.h:6:13: error: API notes replacement type 'double' has a different size from original type 'char'
+
+// CHECK: 5 errors generated.
diff --git a/test/APINotes/types.m b/test/APINotes/types.m
new file mode 100644
index 0000000..fccff80
--- /dev/null
+++ b/test/APINotes/types.m
@@ -0,0 +1,23 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#import <SomeKit/SomeKit.h>
+
+void test(OverriddenTypes *overridden) {
+ int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}}
+
+ int *ip2 = global_int_fun( // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ ip2, // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'double *'}}
+ ip2); // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'float *'}}
+
+ int *ip3 = [overridden // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ methodToMangle: ip3 // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'double *'}}
+ second: ip3]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'float *'}}
+
+ int *ip4 = overridden.intPropertyToMangle; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double *'}}
+}
+
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr' here}}
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr2' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr1' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr2' here}}