Skip to content

Commit 584c6ba

Browse files
Add flag to infer checked pointers types for undefined functions (#691)
Add a new flag (`-infer-types-for-undefs`) that enables inferring pointer types for undefined functions. Undefined functions are still constrained to be internally unchecked, so they can only be rewritten to itypes but never fully checked types. For example: `void foo(int *);` converts to `void foo(int * : itype(_Ptr<int>));`. This change also eliminates some code that was previously duplicated inside `ProgramInfo::link()` for constraining undefined external and static functions. The root cause message emitted for undefined functions has been changed to use the same message for both static and external functions.
1 parent 07c5311 commit 584c6ba

File tree

11 files changed

+277
-72
lines changed

11 files changed

+277
-72
lines changed

clang/include/clang/3C/3CGlobalOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ struct _3COptions {
6767
bool AllowRewriteFailures;
6868

6969
bool ItypesForExtern;
70+
71+
bool InferTypesForUndefs;
7072
};
7173

7274
// NOLINTNEXTLINE(readability-identifier-naming)

clang/include/clang/3C/ProgramInfo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ class ProgramInfo : public ProgramVariableAdder {
269269
// constraint system for that pointer type.
270270
void addVariable(clang::DeclaratorDecl *D,
271271
clang::ASTContext *AstContext) override;
272+
273+
void linkFunction(FunctionVariableConstraint *FV);
272274
};
273275

274276
#endif

clang/include/clang/3C/Utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ bool isInSysHeader(clang::Decl *D);
181181

182182
std::string getSourceText(const clang::SourceRange &SR,
183183
const clang::ASTContext &C);
184+
std::string getSourceText(const clang::CharSourceRange &SR,
185+
const clang::ASTContext &C);
184186

185187
// Find the longest common subsequence.
186188
unsigned longestCommonSubsequence(const char *Str1, const char *Str2,

clang/lib/3C/ConstraintVariables.cpp

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,21 @@ std::string PointerVariableConstraint::tryExtractBaseType(DeclaratorDecl *D,
556556
QualType QT,
557557
const Type *Ty,
558558
const ASTContext &C) {
559-
bool FoundBaseTypeInSrc = false;
560-
if (D && !TSI)
559+
// Implicit parameters declarations from typedef function declarations will
560+
// still have valid and non-empty source ranges, but implicit declarations
561+
// aren't written in the source, so extracting the base type from this range
562+
// gives incorrect type strings. For example, the base type for the implicit
563+
// parameter for `foo_decl` in `typedef void foo(int*); foo foo_decl;` would
564+
// be extracted as "foo_decl", when it should be "int".
565+
if (!D || D->isImplicit())
566+
return "";
567+
568+
if (!TSI)
561569
TSI = D->getTypeSourceInfo();
562-
if (!QT->isOrContainsCheckedType() && !Ty->getAs<TypedefType>() && D && TSI) {
570+
if (!QT->isOrContainsCheckedType() && !Ty->getAs<TypedefType>() && TSI) {
563571
// Try to extract the type from original source to preserve defines
564572
TypeLoc TL = TSI->getTypeLoc();
573+
bool FoundBaseTypeInSrc = false;
565574
if (isa<FunctionDecl>(D)) {
566575
FoundBaseTypeInSrc = D->getAsFunction()->getReturnType() == QT;
567576
TL = getBaseTypeLoc(TL).getAs<FunctionTypeLoc>();
@@ -2220,8 +2229,30 @@ FVComponentVariable::FVComponentVariable(const QualType &QT,
22202229
// parameters similarly to how we do it for pointers in regular function
22212230
// declarations.
22222231
if (D && D->getType() == QT) {
2223-
SourceRange SR = D->getSourceRange();
2224-
SourceDeclaration = SR.isValid() ? getSourceText(SR, C) : "";
2232+
SourceRange DRange = D->getSourceRange();
2233+
if (DRange.isValid()) {
2234+
const SourceManager &SM = C.getSourceManager();
2235+
SourceLocation DLoc = D->getLocation();
2236+
CharSourceRange CSR;
2237+
if (SM.isBeforeInTranslationUnit(DRange.getEnd(), DLoc)) {
2238+
// It's not clear to me why, but the end of the SourceRange for the
2239+
// declaration can come before the SourceLocation for the declaration.
2240+
// This can result in SourceDeclaration failing to capture the whole
2241+
// declaration string, causing rewriting errors. In this case it is also
2242+
// necessary to use CharSourceRange::getCharRange to work around some
2243+
// cases where CharSourceRange::getTokenRange (the default behavior of
2244+
// getSourceText when passed a SourceRange) would not get the full
2245+
// declaration. For example: a parameter declared without a name such as
2246+
// `_Ptr<char>` was captured as `_Ptr`.
2247+
CSR = CharSourceRange::getCharRange(DRange.getBegin(), DLoc);
2248+
} else {
2249+
CSR = CharSourceRange::getTokenRange(DRange);
2250+
}
2251+
2252+
SourceDeclaration = getSourceText(CSR , C);
2253+
} else {
2254+
SourceDeclaration = "";
2255+
}
22252256
}
22262257
}
22272258

clang/lib/3C/DeclRewriter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,14 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) {
567567

568568
// If this is an external function, there is no need to rewrite the
569569
// declaration. We cannot change the signature of external functions.
570-
if (!FDConstraint->hasBody())
570+
// Under the flag -infer-types-for-undef, however, undefined functions do need
571+
// to be rewritten. If the rest of the 3c inference and rewriting code is
572+
// correct, short-circuiting here shouldn't be necessary; the rest of the
573+
// logic in this function should successfully not rewrite undefined functions
574+
// when -infer-types-for-undef is not passed. This assumption could be
575+
// transformed into an assertion if we're confident it won't fail in too many
576+
// places.
577+
if (!_3COpts.InferTypesForUndefs && !FDConstraint->hasBody())
571578
return true;
572579

573580
// RewriteParams and RewriteReturn track if we will need to rewrite the

clang/lib/3C/ProgramInfo.cpp

Lines changed: 37 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -417,78 +417,54 @@ bool ProgramInfo::link() {
417417

418418
// For every global function that is an unresolved external, constrain
419419
// its parameter types to be wild. Unless it has a bounds-safe annotation.
420-
for (const auto &U : ExternalFunctionFVCons) {
421-
std::string FuncName = U.first;
422-
FVConstraint *G = U.second;
423-
424-
// If there was a checked type on a variable in the input program, it
425-
// should stay that way. Otherwise, we shouldn't be adding a checked type
426-
// to an extern function.
427-
std::string Rsn = (G->hasBody() ? ""
428-
: "Unchecked pointer in parameter or "
429-
"return of external function " +
430-
FuncName);
431-
432-
// Handle the cases where itype parameters should not be treated as their
433-
// unchecked type.
434-
// TODO: Ditto re getting a PSL (in the case in which Rsn is non-empty and
435-
// it is actually used).
436-
G->equateWithItype(*this, Rsn, nullptr);
437-
438-
// If we've seen this symbol, but never seen a body for it, constrain
439-
// everything about it.
440-
// Some global symbols we don't need to constrain to wild, like
441-
// malloc and free. Check those here and skip if we find them.
442-
if (!G->hasBody()) {
443-
const FVComponentVariable *Ret = G->getCombineReturn();
444-
Ret->getInternal()->constrainToWild(CS, Rsn);
445-
if (!Ret->getExternal()->srcHasItype() &&
446-
!Ret->getExternal()->isGeneric())
447-
Ret->getExternal()->constrainToWild(CS, Rsn);
448-
449-
for (unsigned I = 0; I < G->numParams(); I++) {
450-
const FVComponentVariable *Param = G->getCombineParam(I);
451-
Param->getInternal()->constrainToWild(CS, Rsn);
452-
if (!Param->getExternal()->srcHasItype() &&
453-
!Param->getExternal()->isGeneric())
454-
Param->getExternal()->constrainToWild(CS, Rsn);
455-
}
456-
}
457-
}
420+
for (const auto &U : ExternalFunctionFVCons)
421+
linkFunction(U.second);
458422

459423
// Repeat for static functions.
460424
//
461425
// Static functions that don't have a body will always cause a linking
462426
// error during compilation. They may still be useful as code is developed,
463427
// so we treat them as if they are external, and constrain parameters
464428
// to wild as appropriate.
465-
for (const auto &U : StaticFunctionFVCons) {
466-
for (const auto &V : U.second) {
429+
for (const auto &U : StaticFunctionFVCons)
430+
for (const auto &V : U.second)
431+
linkFunction(V.second);
467432

468-
std::string FileName = U.first;
469-
std::string FuncName = V.first;
470-
FVConstraint *G = V.second;
471-
472-
std::string Rsn = (G->hasBody() ? ""
473-
: "Unchecked pointer in parameter or "
474-
"return of static function " +
475-
FuncName + " in " + FileName);
476-
477-
// TODO: Ditto re getting a PSL
478-
G->equateWithItype(*this, Rsn, nullptr);
433+
return true;
434+
}
479435

480-
if (!G->hasBody()) {
436+
void ProgramInfo::linkFunction(FunctionVariableConstraint *FV) {
437+
// If there was a checked type on a variable in the input program, it
438+
// should stay that way. Otherwise, we shouldn't be adding a checked type
439+
// to an undefined function.
440+
std::string Rsn = (FV->hasBody() ? "" : "Unchecked pointer in parameter or "
441+
"return of undefined function " +
442+
FV->getName());
443+
444+
// Handle the cases where itype parameters should not be treated as their
445+
// unchecked type.
446+
// TODO: Ditto re getting a PSL (in the case in which Rsn is non-empty and
447+
// it is actually used).
448+
FV->equateWithItype(*this, Rsn, nullptr);
449+
450+
// Used to apply constraints to parameters and returns for function without a
451+
// body. In the default configuration, the function is fully constrained so
452+
// that parameters and returns are considered unchecked. When 3C is run with
453+
// --infer-types-for-undefs, only internal variables are constrained, allowing
454+
// external variables to solve to checked types meaning the parameter will be
455+
// rewritten to an itype.
456+
auto LinkComponent = [this, Rsn](const FVComponentVariable *FVC) {
457+
FVC->getInternal()->constrainToWild(CS, Rsn);
458+
if (!_3COpts.InferTypesForUndefs &&
459+
!FVC->getExternal()->srcHasItype() && !FVC->getExternal()->isGeneric())
460+
FVC->getExternal()->constrainToWild(CS, Rsn);
461+
};
481462

482-
if (!G->getExternalReturn()->isGeneric())
483-
G->getExternalReturn()->constrainToWild(CS, Rsn);
484-
for (unsigned I = 0; I < G->numParams(); I++)
485-
if (!G->getExternalParam(I)->isGeneric())
486-
G->getExternalParam(I)->constrainToWild(CS, Rsn);
487-
}
488-
}
463+
if (!FV->hasBody()) {
464+
LinkComponent(FV->getCombineReturn());
465+
for (unsigned I = 0; I < FV->numParams(); I++)
466+
LinkComponent(FV->getCombineParam(I));
489467
}
490-
491-
return true;
492468
}
493469

494470
// Populate Variables, VarDeclToStatement, RVariables, and DepthMap with

clang/lib/3C/Utils.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,15 @@ bool isInSysHeader(clang::Decl *D) {
387387

388388
std::string getSourceText(const clang::SourceRange &SR,
389389
const clang::ASTContext &C) {
390+
return getSourceText(CharSourceRange::getTokenRange(SR), C);
391+
}
392+
393+
std::string getSourceText(const clang::CharSourceRange &SR,
394+
const clang::ASTContext &C) {
390395
assert(SR.isValid() && "Invalid Source Range requested.");
391396
auto &SM = C.getSourceManager();
392397
auto LO = C.getLangOpts();
393-
llvm::StringRef Srctxt =
394-
Lexer::getSourceText(CharSourceRange::getTokenRange(SR), SM, LO);
398+
llvm::StringRef Srctxt = Lexer::getSourceText(SR, SM, LO);
395399
return Srctxt.str();
396400
}
397401

clang/test/3C/implicit_casts_root_cause.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ void test_no_cause() {
1111
has_void(c);
1212
}
1313

14-
void has_float(float* v); // expected-warning {{Unchecked pointer in parameter or return of external function has_float}}
14+
void has_float(float* v); // expected-warning {{Unchecked pointer in parameter or return of undefined function has_float}}
1515
void test_float_cause() {
1616
int *b, *c;
1717
has_float(b); // expected-warning {{1 unchecked pointer: Cast from int * to float *}}

0 commit comments

Comments
 (0)