Skip to content

Allowing multiple definitions of the same function #505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b5d212e
Allowing multiple definitions of the same function
mwhicks1 Mar 20, 2021
1222c7c
Constrain second main() arg to ARR<NTARR>
mwhicks1 Mar 21, 2021
d2967aa
Partial support for permissive main() merging
mwhicks1 Mar 22, 2021
f1f8e51
Test update
mwhicks1 Mar 23, 2021
91efdb6
Merge remote-tracking branch 'origin/main' into multi-def-ok
kyleheadley Mar 24, 2021
21329d2
comment
kyleheadley Mar 25, 2021
f4df7ce
remove unreachable branch
kyleheadley Mar 25, 2021
4fd347a
add name to mkString and allow null decls
kyleheadley Mar 29, 2021
ac7f797
don't add
kyleheadley Mar 29, 2021
4d7731e
handle typed but unnamed params
kyleheadley Mar 29, 2021
ed3d5fa
restore (and tweak) some no-change code
kyleheadley Mar 29, 2021
393e570
avoid param lookup failures
kyleheadley Mar 30, 2021
3fe1741
remove unused code (NewFucSig)
kyleheadley Mar 30, 2021
9e547d7
merge with main
kyleheadley Mar 30, 2021
e641689
add name to function pointers in tests
kyleheadley Mar 30, 2021
19eda1d
remove old tests that no longer fail
kyleheadley Mar 30, 2021
15d9b42
fix names in tests
kyleheadley Mar 31, 2021
bb47ba4
handle typedefs better
kyleheadley Mar 31, 2021
baa40b6
allow merge with differing param count; improve function pointer rewr…
kyleheadley Apr 1, 2021
cc81489
recognize compile errors
kyleheadley Apr 1, 2021
a77b3ff
generate tests with less fp spaces
kyleheadley Apr 1, 2021
e7957f5
manual change for fp spacing in tests
kyleheadley Apr 1, 2021
5427010
split types test into two files
kyleheadley Apr 1, 2021
87492f1
remove debug mark
kyleheadley Apr 1, 2021
5d63624
remove unused code
kyleheadley Apr 1, 2021
452c58f
add tests
kyleheadley Apr 1, 2021
4fb6d52
Favor having a body in merges if same param count
kyleheadley Apr 1, 2021
848771c
implement PR suggestions
kyleheadley Apr 2, 2021
4ab054e
restore special handling of typedef-declared functions
kyleheadley Apr 2, 2021
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
16 changes: 11 additions & 5 deletions clang/include/clang/3C/ConstraintVariables.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class ConstraintVariable {
// to be used inside an itype.
virtual std::string mkString(Constraints &CS, bool EmitName = true,
bool ForItype = false, bool EmitPointee = false,
bool UnmaskTypedef = false) const = 0;
bool UnmaskTypedef = false,
std::string UseName = "") const = 0;

// Debug printing of the constraint variable.
virtual void print(llvm::raw_ostream &O) const = 0;
Expand Down Expand Up @@ -401,7 +402,8 @@ class PointerVariableConstraint : public ConstraintVariable {

std::string mkString(Constraints &CS, bool EmitName = true,
bool ForItype = false, bool EmitPointee = false,
bool UnmaskTypedef = false) const override;
bool UnmaskTypedef = false,
std::string UseName = "") const override;

FunctionVariableConstraint *getFV() const { return FV; }

Expand All @@ -413,6 +415,8 @@ class PointerVariableConstraint : public ConstraintVariable {
void constrainToWild(Constraints &CS, const std::string &Rsn,
PersistentSourceLoc *PL) const override;
void constrainOuterTo(Constraints &CS, ConstAtom *C, bool DoLB = false);
void constrainIdxTo(Constraints &CS, ConstAtom *C,
unsigned int Idx, bool DoLB = false);
bool anyChanges(const EnvironmentMap &E) const override;
bool anyArgumentIsWild(const EnvironmentMap &E);
bool hasWild(const EnvironmentMap &E, int AIdx = -1) const override;
Expand Down Expand Up @@ -471,8 +475,9 @@ class FVComponentVariable {
void mergeDeclaration(FVComponentVariable *From, ProgramInfo &I,
std::string &ReasonFailed);
std::string mkItypeStr(Constraints &CS) const;
std::string mkTypeStr(Constraints &CS, bool EmitName) const;
std::string mkString(Constraints &CS) const;
std::string mkTypeStr(Constraints &CS, bool EmitName,
std::string UseName = "") const;
std::string mkString(Constraints &CS, bool EmitName = true) const;

bool hasItypeSolution(Constraints &CS) const;
bool hasCheckedSolution(Constraints &CS) const;
Expand Down Expand Up @@ -575,7 +580,8 @@ class FunctionVariableConstraint : public ConstraintVariable {

std::string mkString(Constraints &CS, bool EmitName = true,
bool ForItype = false, bool EmitPointee = false,
bool UnmaskTypedef = false) const override;
bool UnmaskTypedef = false,
std::string UseName = "") const override;
void print(llvm::raw_ostream &O) const override;
void dump() const override { print(llvm::errs()); }
void dumpJson(llvm::raw_ostream &O) const override;
Expand Down
18 changes: 4 additions & 14 deletions clang/include/clang/3C/DeclRewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ class DeclRewriter {
// declaration in the containing multi-decl is visited.
RSet VisitedMultiDeclMembers;

// TODO: I don't like having this be static, but it needs to be static in
// order to pass information between different translation units. A
// new instance of this class (and the RewriteConsumer class) is created
// for each translation unit.
static std::map<std::string, std::string> NewFuncSig;

// Visit each Decl in ToRewrite and apply the appropriate pointer type
// to that Decl. ToRewrite is the set of all declarations to rewrite.
void rewrite(RSet &ToRewrite);
Expand Down Expand Up @@ -87,10 +81,9 @@ class DeclRewriter {
class FunctionDeclBuilder : public RecursiveASTVisitor<FunctionDeclBuilder> {
public:
explicit FunctionDeclBuilder(ASTContext *C, ProgramInfo &I, RSet &DR,
std::map<std::string, std::string> &NewFuncSig,
ArrayBoundsRewriter &ArrRewriter)
: Context(C), Info(I), RewriteThese(DR), ABRewriter(ArrRewriter),
VisitedSet(), ModifiedFuncSignatures(NewFuncSig) {}
VisitedSet() {}

bool VisitFunctionDecl(FunctionDecl *);
bool isFunctionVisited(std::string FuncName);
Expand All @@ -105,19 +98,16 @@ class FunctionDeclBuilder : public RecursiveASTVisitor<FunctionDeclBuilder> {
// Used to ensure the new signature is only computed once for each function.
std::set<std::string> VisitedSet;

// This is a map from functions (the string representation of their names) to
// their function signature in the rewritten program.
std::map<std::string, std::string> &ModifiedFuncSignatures;

// Get existing itype string from constraint variables.
std::string getExistingIType(ConstraintVariable *DeclC);

virtual void buildDeclVar(const FVComponentVariable *CV,
DeclaratorDecl *Decl, std::string &Type,
std::string &IType, bool &RewriteParm,
bool &RewriteRet);
std::string &IType, std::string UseName,
bool &RewriteParm, bool &RewriteRet);
void buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl,
std::string &Type, std::string &IType,
std::string UseName,
bool &RewriteParm, bool &RewriteRet);
void buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl,
std::string &Type, std::string &IType, bool &RewriteParm,
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/3C/3C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,15 @@ bool _3CInterface::parseASTs() {
Tool->appendArgumentsAdjuster(addVerifyAdjuster());

// load the ASTs
return !Tool->buildASTs(ASTs);
if (Tool->buildASTs(ASTs))
return false;

// check for compile errors
unsigned int Errs = 0;
for (auto &TU : ASTs)
Errs += TU->getDiagnostics().getClient()->getNumErrors();

return Errs == 0;
}

bool _3CInterface::addVariables() {
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/3C/AVarBoundsInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,11 @@ bool AvarBoundsInference::inferBounds(BoundsKey K, AVarGraph &BKGraph, bool From

bool AVarBoundsInfo::isValidBoundVariable(clang::Decl *D) {
// All parameters, return, and field values are valid bound variables.
if (isa<ParmVarDecl>(D) || isa<FunctionDecl>(D) || isa<FieldDecl>(D))
if (D && (isa<ParmVarDecl>(D) || isa<FunctionDecl>(D) || isa<FieldDecl>(D)))
return true;

// For VarDecls, check if these are are not dummy and have a name.
if (auto *VD = dyn_cast<VarDecl>(D))
if (auto *VD = dyn_cast_or_null<VarDecl>(D))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it that D could now be nullptr here when it couldn't before? Or maybe it could, and you are fixing a bug? When could that happen?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started explicitly passing null when needing a param for a prototype that doesn't exist i.e int *foo(). This code is called from that section, so it needed null checks. Previously, it may have been worthy of an assert.

return !VD->getNameAsString().empty();

return false;
Expand Down
114 changes: 71 additions & 43 deletions clang/lib/3C/ConstraintVariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,19 @@ std::string PointerVariableConstraint::gatherQualStrings(void) const {
std::string PointerVariableConstraint::mkString(Constraints &CS,
bool EmitName, bool ForItype,
bool EmitPointee,
bool UnmaskTypedef) const {
bool UnmaskTypedef,
std::string UseName) const {

// The name field encodes if this variable is the return type for a function.
// TODO: store this information in a separate field.
bool IsReturn = getName() == RETVAR;

if (UseName.empty())
UseName = getName();

if (IsTypedef && !UnmaskTypedef) {
return gatherQualStrings() + TypedefString +
(EmitName && getName() != RETVAR ? (" " + getName()) : " ");
(EmitName && !IsReturn ? (" " + UseName) : " ");
}

std::ostringstream Ss;
Expand All @@ -699,7 +708,7 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
bool AllArrays = true;
// Are we in a sequence of arrays
bool ArrayRun = false;
if (!EmitName || getName() == RETVAR)
if (!EmitName || IsReturn)
EmittedName = true;
uint32_t TypeIdx = 0;

Expand Down Expand Up @@ -736,7 +745,7 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
if (PrevArr && ArrSizes.at(TypeIdx).first != O_SizedArray && !EmittedName) {
EmittedName = true;
addArrayAnnotations(CheckedArrs, EndStrs);
EndStrs.push_front(" " + getName());
EndStrs.push_front(" " + UseName);
}
PrevArr = ((K == Atom::A_Arr || K == Atom::A_NTArr) && ArrPresent &&
ArrSizes.at(TypeIdx).first == O_SizedArray);
Expand Down Expand Up @@ -801,7 +810,9 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
assert(BaseType.size() > 0);
EmittedBase = true;
if (FV) {
Ss << FV->mkString(CS);
Ss << FV->mkString(CS, EmitName, ForItype,
EmitPointee, UnmaskTypedef, UseName);
EmittedName |= EmitName;
} else {
Ss << BaseType << " *";
}
Expand All @@ -828,14 +839,14 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
// the the stack array type.
if (PrevArr && !EmittedName && AllArrays) {
EmittedName = true;
EndStrs.push_front(" " + getName());
EndStrs.push_front(" " + UseName);
}

if (!EmittedBase) {
// If we have a FV pointer, then our "base" type is a function pointer.
// type.
if (FV) {
Ss << FV->mkString(CS);
Ss << FV->mkString(CS, false);
} else if (TypedefLevelInfo.HasTypedef) {
std::ostringstream Buf;
getQualString(TypedefLevelInfo.TypedefLevel, Buf);
Expand All @@ -852,8 +863,8 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
}

// No space after itype.
if (!EmittedName)
Ss << " " << getName();
if (!EmittedName && !UseName.empty())
Ss << " " << UseName;

// Final array dropping.
if (!CheckedArrs.empty()) {
Expand All @@ -863,8 +874,7 @@ std::string PointerVariableConstraint::mkString(Constraints &CS,
Ss << Str;
}

// TODO Remove comparison to RETVAR.
if (getName() == RETVAR && !ForItype)
if (IsReturn && !ForItype)
Ss << " ";

return Ss.str();
Expand Down Expand Up @@ -1145,12 +1155,12 @@ void PointerVariableConstraint::constrainToWild(Constraints &CS,
FV->constrainToWild(CS, Rsn, PL);
}

void PointerVariableConstraint::constrainOuterTo(Constraints &CS, ConstAtom *C,
bool DoLB) {
void PointerVariableConstraint::constrainIdxTo(Constraints &CS, ConstAtom *C,
unsigned int Idx, bool DoLB) {
assert(C == CS.getPtr() || C == CS.getArr() || C == CS.getNTArr());

if (Vars.size() > 0) {
Atom *A = *Vars.begin();
if (Vars.size() > Idx) {
Atom *A = Vars[Idx];
if (VarAtom *VA = dyn_cast<VarAtom>(A)) {
if (DoLB)
CS.addConstraint(CS.createGeq(VA, C, false));
Expand All @@ -1172,6 +1182,11 @@ void PointerVariableConstraint::constrainOuterTo(Constraints &CS, ConstAtom *C,
}
}

void PointerVariableConstraint::constrainOuterTo(Constraints &CS, ConstAtom *C,
bool DoLB) {
constrainIdxTo(CS,C,0,DoLB);
}

bool PointerVariableConstraint::anyArgumentIsWild(const EnvironmentMap &E) {
for (auto *ArgVal : ArgumentConstraints) {
if (!ArgVal->isChecked(E)) {
Expand Down Expand Up @@ -1434,12 +1449,19 @@ bool FunctionVariableConstraint::solutionEqualTo(Constraints &CS,
std::string FunctionVariableConstraint::mkString(Constraints &CS,
bool EmitName, bool ForItype,
bool EmitPointee,
bool UnmaskTypedef) const {
bool UnmaskTypedef,
std::string UseName) const {
if (UseName.empty())
UseName = Name;
std::string Ret = ReturnVar.mkTypeStr(CS, false);
std::string Itype = ReturnVar.mkItypeStr(CS);
// This is done to rewrite the typedef of a function proto
if (UnmaskTypedef && EmitName)
Ret += Name;
if (EmitName) {
if (UnmaskTypedef)
// This is done to rewrite the typedef of a function proto
Ret += UseName;
else
Ret += "(*" + UseName + ")";
}
Ret = Ret + "(";
std::vector<std::string> ParmStrs;
for (const auto &I : this->ParamVars)
Expand Down Expand Up @@ -1793,6 +1815,8 @@ void PointerVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV,
assert(Vars.size() == NewVatoms.size() &&
"Merging error, pointer depth change");
Vars = NewVatoms;
if (Name.empty())
Name = From->Name;
SrcHasItype = SrcHasItype || From->SrcHasItype;
if (!From->ItypeStr.empty())
ItypeStr = From->ItypeStr;
Expand Down Expand Up @@ -1834,6 +1858,12 @@ void FunctionVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV,

FVConstraint *From = dyn_cast<FVConstraint>(FromCV);
assert(From != nullptr);
assert("this should have more params" &&
this->numParams() >= From->numParams());

// transferable basic info
Hasbody |= From->Hasbody;
if (Name.empty()) Name = From->Name;

// Merge returns.
ReturnVar.mergeDeclaration(&From->ReturnVar, I, ReasonFailed);
Expand All @@ -1842,27 +1872,13 @@ void FunctionVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV,
return;
}

if (From->numParams() == 0) {
// From is an untyped declaration, and adds no information.
return;
}
if (this->numParams() == 0) {
// This is an untyped declaration, we need to switch to the other
assert(false && "This merge should happen in reverse");
From->mergeDeclaration(this, I, ReasonFailed);
} else {
// Standard merge.
if (this->numParams() != From->numParams()) {
ReasonFailed = "differing number of arguments";
// Merge params.
for (unsigned J = 0; J < From->numParams(); J++) {
ParamVars[J].mergeDeclaration(&From->ParamVars[J], I, ReasonFailed);
if (ReasonFailed != "") {
ReasonFailed += " for parameter " + std::to_string(J);
return;
}
for (unsigned J = 0; J < From->numParams(); J++) {
ParamVars[J].mergeDeclaration(&From->ParamVars[J], I, ReasonFailed);
if (ReasonFailed != "") {
ReasonFailed += " for parameter " + std::to_string(J);
return;
}
}
}
}

Expand Down Expand Up @@ -1896,16 +1912,28 @@ void FVComponentVariable::mergeDeclaration(FVComponentVariable *From,
}

std::string
FVComponentVariable::mkString(Constraints &CS) const {
return mkTypeStr(CS, true) + mkItypeStr(CS);
FVComponentVariable::mkString(Constraints &CS, bool EmitName) const {
return mkTypeStr(CS, EmitName) + mkItypeStr(CS);
}

std::string
FVComponentVariable::mkTypeStr(Constraints &CS, bool EmitName) const {
if (hasCheckedSolution(CS))
return ExternalConstraint->mkString(CS, EmitName);
FVComponentVariable::mkTypeStr(Constraints &CS, bool EmitName,
std::string UseName) const {
// if checked or given new name, generate type
if (hasCheckedSolution(CS) || (EmitName && !UseName.empty()))
return ExternalConstraint->mkString(CS, EmitName, false,
false, false, UseName);
// if no need to generate type, try to use source
if (!SourceDeclaration.empty())
return SourceDeclaration;
// if no source and no name, generate nameless type
if (EmitName && ExternalConstraint->getName().empty())
return ExternalConstraint->getOriginalTy();
// if no source and a have a needed name, generate named type
if (EmitName)
return ExternalConstraint->getRewritableOriginalTy()
+ ExternalConstraint->getName();
// if no source and don't need a name, generate type ready for one
return ExternalConstraint->getRewritableOriginalTy();
}

Expand Down
Loading