Skip to content

Commit 7a73aa6

Browse files
authored
Add support for _Return_value expressions (#544)
A_Return_value expression allows programmers to name the value returned by a function in a return bounds declaration. This changes extends the clang IR to represent _Return_value expressions and _Current_expr_value expressions. It also adds lexing, parsing, and type system support for _Return_value expressions. This addresses the first part of issue #205. We create a new expression class BoundsValueExpr to represent these expressions. This class is modelled on CXXThisExpr. Constructing a_Return_value expression requires knowing the return type of the function that the return bounds is associated with. We must have the _Return_value expression constructed before we can build the function type, since function types incorporate the return bounds expressions. Unfortunately, we don't know the function return type until the middle of GetFullTypeForDeclarator in SemaType.cpp. Because clang intertwines AST building, type checking, and other static semantic checking, we have to "defer parse" return bounds expressions. "Defer parse" means that the compiler recognize the tokens that make up a return bounds expression, saves them, and parses them later to build the IR for the return bounds expressions. To allow parsing of the return bounds to happen in the middle of SemaType.cpp, we introduce a callback from semantic checking to the parser. This lets us preserve the interface between the two. We have to bring the parameters into scope, since they've already gone out of scope by the time GetFullTypeForDeclarator runs. We aren't the first people to have to do this. Other parsing phases such as parsing of attributes have to do this too. After parsing the return bounds expression, we attach it to the declarator whose type is being constructed. This lets us get the line number information right for error messages involving redeclarations with conflicting return bounds. (Long explanation: memoization of function types also memoizes bounds expressions embedded in types. It does this by ignoring line numbers and abstracting function parameters to parameter locations. The end result is that line numbers for expressions embedded in types are meaningless. Clang has a complicated mechanism for tracking source line information for types called TypeSourceInfo, but we haven't implemented support for embedded expressions in types and are not likely to anytime soon. This change also contains some unrelated clean up: 1. Fix the line ending for the test case for bug526 to be line feeds. 2. Delete identifiers for _Dynamic_bounds_cast and _Assume_bounds_cast. These are keywords, so we don't need identifiers. Testing: - There will be a separate pull request to the Checked C repo for parsing and typechecking tests for_Return_value. - Added clang tests for AST dumping and checking of bounds declarations. The checking of bounds declarations involving _Return_value doesn't work yet, so the tests document this. - Passes automated Checked C and clang testing with these changes.
1 parent 4dc7631 commit 7a73aa6

36 files changed

+375
-86
lines changed

include/clang/AST/CanonBounds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ namespace clang {
9999
Result CompareImpl(const PositionalParameterExpr *E1,
100100
const PositionalParameterExpr *E2);
101101
Result CompareImpl(const BoundsCastExpr *E1, const BoundsCastExpr *E2);
102+
Result CompareImpl(const BoundsValueExpr *E1, const BoundsValueExpr *E2);
102103
Result CompareImpl(const AtomicExpr *E1, const AtomicExpr *E2);
103104
Result CompareImpl(const BlockExpr *E1, const BlockExpr *E2);
104105

include/clang/AST/Expr.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,45 @@ class PositionalParameterExpr : public Expr {
54415441
}
54425442
};
54435443

5444+
// \brief Represents the \c _Currrent_expr_value and _Return_value expressions
5445+
// in Checked C. These expressions can be used within bounds expressions.
5446+
class BoundsValueExpr : public Expr {
5447+
public:
5448+
enum Kind {
5449+
Current,
5450+
Return
5451+
};
5452+
5453+
private:
5454+
SourceLocation Loc;
5455+
Kind ValueExprKind;
5456+
5457+
public:
5458+
BoundsValueExpr(SourceLocation L, QualType Type, Kind K)
5459+
: Expr(BoundsValueExprClass, Type, VK_RValue, OK_Ordinary,
5460+
false, false, false, false), Loc(L), ValueExprKind(K) { }
5461+
5462+
BoundsValueExpr(EmptyShell Empty) : Expr(BoundsValueExprClass, Empty) {}
5463+
5464+
SourceLocation getLocation() const { return Loc; }
5465+
void setLocation(SourceLocation L) { Loc = L; }
5466+
5467+
SourceLocation getLocStart() const LLVM_READONLY { return Loc; }
5468+
SourceLocation getLocEnd() const LLVM_READONLY { return Loc; }
5469+
5470+
Kind getKind() const { return ValueExprKind; }
5471+
void setKind(Kind K) { ValueExprKind = K; }
5472+
5473+
static bool classof(const Stmt *T) {
5474+
return T->getStmtClass() == BoundsValueExprClass;
5475+
}
5476+
5477+
// Iterators
5478+
child_range children() {
5479+
return child_range(child_iterator(), child_iterator());
5480+
}
5481+
};
5482+
54445483

54455484
//===----------------------------------------------------------------------===//
54465485
// Clang Extensions

include/clang/AST/RecursiveASTVisitor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,7 @@ DEF_TRAVERSE_STMT(NullaryBoundsExpr, {})
25552555
DEF_TRAVERSE_STMT(RangeBoundsExpr, {})
25562556
DEF_TRAVERSE_STMT(InteropTypeExpr, {})
25572557
DEF_TRAVERSE_STMT(PositionalParameterExpr, {})
2558+
DEF_TRAVERSE_STMT(BoundsValueExpr, {})
25582559

25592560
// For coroutines expressions, traverse either the operand
25602561
// as written or the implied calls, depending on what the

include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9548,6 +9548,9 @@ def err_bounds_type_annotation_lost_checking : Error<
95489548
"out-of-scope variable for bounds in a function type (a function type can "
95499549
"only reference parameters from its own parameter list)">;
95509550

9551+
def err_return_value_not_in_scope : Error<
9552+
"_Return_value can be used only in a return bounds expression">;
9553+
95519554
def err_expected_bounds : Error<
95529555
"expression has unknown bounds">;
95539556

include/clang/Basic/StmtNodes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def RangeBoundsExpr : DStmt<BoundsExpr>;
183183
def InteropTypeExpr : DStmt<Expr>;
184184
def PositionalParameterExpr : DStmt<Expr>;
185185
def BoundsCastExpr : DStmt<ExplicitCastExpr>;
186+
def BoundsValueExpr : DStmt<Expr>;
186187

187188
// CUDA Expressions.
188189
def CUDAKernelCallExpr : DStmt<CallExpr>;

include/clang/Basic/TokenKinds.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ KEYWORD(_Assume_bounds_cast , KEYCHECKEDC)
654654
KEYWORD(_Dynamic_bounds_cast , KEYCHECKEDC)
655655
KEYWORD(_For_any , KEYCHECKEDC)
656656
KEYWORD(_Itype_for_any , KEYCHECKEDC)
657+
KEYWORD(_Current_expr_value , KEYCHECKEDC)
658+
KEYWORD(_Return_value , KEYCHECKEDC)
657659

658660
// Borland Extensions which should be disabled in strict conformance mode.
659661
ALIAS("_pascal" , __pascal , KEYBORLAND)

include/clang/Parse/Parser.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,6 @@ class Parser : public CodeCompletionHandler {
179179
/// \brief Identifier for "rel_align_value"
180180
IdentifierInfo *Ident_rel_align_value;
181181

182-
/// \brief Identifier for "dynamic_bounds_cast"
183-
IdentifierInfo *Ident_dynamic_bounds_cast;
184-
185-
/// \brief Identifier for "assume_bounds_cast"
186-
IdentifierInfo *Ident_assume_bounds_cast;
187-
188182
enum CheckedScopeKind {
189183
/// '{'
190184
CSK_None,
@@ -1780,6 +1774,18 @@ class Parser : public CodeCompletionHandler {
17801774
BoundsAnnotations &Result,
17811775
const Declarator &D);
17821776

1777+
// Delay parse a return bounds expression in Toks. Used to parse return
1778+
// bounds after the return type has been constructed. Stores the bounds
1779+
// expression in Result. Returns true if there was a parsing error.
1780+
static bool ParseBoundsCallback(void *P,
1781+
std::unique_ptr<CachedTokens> Toks,
1782+
ArrayRef<ParmVarDecl *> Params,
1783+
BoundsAnnotations &Result,
1784+
const Declarator &D);
1785+
1786+
ExprResult ParseReturnValueExpression();
1787+
1788+
17831789
//===--------------------------------------------------------------------===//
17841790
// clang Expressions
17851791

include/clang/Sema/DeclSpec.h

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,11 +1394,11 @@ struct DeclaratorChunk {
13941394
ParamInfo *Params;
13951395

13961396
/// The annotations for the value returned by the function. We store them
1397-
// as individual fields, not BoundsAnnotations, because BoundsAnnotations has
1398-
// a default constructor. This is for good reason. For equally good reasons,
1399-
// DeclaratorChunk doesn't have a default constructor. This means that using
1400-
// a default initialized instance of BoundsAnnotations is not allowed here.
1401-
BoundsExpr *ReturnBounds;
1397+
// as individual fields because the return bounds are deferred-parsed.
1398+
// Note: ReturnBounds is actually a unique_ptr. However unique_ptr requires
1399+
// a constructor and this struct can't have one, so we cast it to a
1400+
// a regular pointer type.
1401+
CachedTokens *ReturnBounds;
14021402
InteropTypeExpr *ReturnInteropType;
14031403

14041404
union {
@@ -1547,11 +1547,6 @@ struct DeclaratorChunk {
15471547

15481548
/// \brief Get the trailing-return-type for this function declarator.
15491549
ParsedType getTrailingReturnType() const { return TrailingReturnType; }
1550-
1551-
/// \brief The bounds annotations for the return value
1552-
BoundsAnnotations getReturnAnnots() const {
1553-
return BoundsAnnotations(ReturnBounds, ReturnInteropType);
1554-
}
15551550
};
15561551

15571552
struct BlockPointerTypeInfo : TypeInfoCommon {
@@ -1697,7 +1692,8 @@ struct DeclaratorChunk {
16971692
SourceLocation LocalRangeBegin,
16981693
SourceLocation LocalRangeEnd,
16991694
SourceLocation ReturnAnnotsColonLoc,
1700-
BoundsAnnotations &ReturnAnnotsExpr,
1695+
InteropTypeExpr *ReturnInteorpTypeExpr,
1696+
std::unique_ptr<CachedTokens> ReturnBounds,
17011697
Declarator &TheDeclarator,
17021698
TypeResult TrailingReturnType =
17031699
TypeResult());
@@ -1906,6 +1902,8 @@ class Declarator {
19061902
/// \brief The asm label, if specified.
19071903
Expr *AsmLabel;
19081904

1905+
/// \brief The return bounds for a function declarator.
1906+
BoundsExpr *ReturnBounds;
19091907
#ifndef _MSC_VER
19101908
union {
19111909
#endif
@@ -1935,7 +1933,8 @@ class Declarator {
19351933
GroupingParens(false), FunctionDefinition(FDK_Declaration),
19361934
Redeclaration(false), Extension(false), ObjCIvar(false),
19371935
ObjCWeakProperty(false), InlineStorageUsed(false),
1938-
Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr) {}
1936+
Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr),
1937+
ReturnBounds(nullptr) {}
19391938

19401939
~Declarator() {
19411940
clear();
@@ -2500,6 +2499,9 @@ class Declarator {
25002499
void setAsmLabel(Expr *E) { AsmLabel = E; }
25012500
Expr *getAsmLabel() const { return AsmLabel; }
25022501

2502+
void setReturnBounds(BoundsExpr *E) { ReturnBounds = E; }
2503+
BoundsExpr *getReturnBounds() const { return ReturnBounds; }
2504+
25032505
void setExtension(bool Val = true) { Extension = Val; }
25042506
bool getExtension() const { return Extension; }
25052507

include/clang/Sema/Sema.h

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,9 @@ class Sema {
23442344
void ActOnReenterFunctionContext(Scope* S, Decl* D);
23452345
void ActOnExitFunctionContext();
23462346

2347+
/// Push the parameters listed in Params into scope.
2348+
void ActOnSetupParametersAgain(Scope* S, ArrayRef<ParmVarDecl *> Params);
2349+
23472350
DeclContext *getFunctionLevelDeclContext();
23482351

23492352
/// getCurFunctionDecl - If inside of a function body, this returns a pointer
@@ -4666,6 +4669,45 @@ class Sema {
46664669
// \#pragma CHECKED_SCOPE.
46674670
void ActOnPragmaCheckedScope(Scope *S, tok::OnOffSwitch OOS);
46684671

4672+
BoundsExpr *CreateInvalidBoundsExpr();
4673+
/// /brief Synthesize the interop type expression implied by the presence
4674+
/// of a bounds expression. Ty is the original unchecked type. Returns null
4675+
/// if none exists.
4676+
InteropTypeExpr *SynthesizeInteropTypeExpr(QualType Ty, bool IsParam);
4677+
BoundsExpr *CreateCountForArrayType(QualType QT);
4678+
4679+
// _Return_value in Checked C bounds expressions.
4680+
ExprResult ActOnReturnValueExpr(SourceLocation Loc);
4681+
4682+
/// \brief When non-NULL, the type of the '_Return_value' expression.
4683+
QualType BoundsExprReturnValue;
4684+
4685+
/// \brief RAII object used to temporarily set the the type of _Return_value
4686+
class CheckedCReturnValueRAII {
4687+
Sema &S;
4688+
QualType OldReturnValue;
4689+
public:
4690+
CheckedCReturnValueRAII(Sema &S, QualType ReturnVal) : S(S) {
4691+
OldReturnValue = S.BoundsExprReturnValue;
4692+
S.BoundsExprReturnValue = ReturnVal;
4693+
}
4694+
4695+
~CheckedCReturnValueRAII() {
4696+
S.BoundsExprReturnValue = OldReturnValue;
4697+
}
4698+
};
4699+
4700+
typedef bool
4701+
(*ParseDeferredBoundsCallBackFn)(void *P,
4702+
std::unique_ptr<CachedTokens> Toks,
4703+
ArrayRef<ParmVarDecl *> Params,
4704+
BoundsAnnotations &Result,
4705+
const Declarator &D);
4706+
void SetDeferredBoundsCallBack(void *OpaqueData, ParseDeferredBoundsCallBackFn p);
4707+
4708+
ParseDeferredBoundsCallBackFn DeferredBoundsParser;
4709+
void *DeferredBoundsParserData;
4710+
46694711
// Represents the context where an expression must be non-modifying.
46704712
enum NonModifyingContext {
46714713
NMC_Unknown,
@@ -4679,13 +4721,6 @@ class Sema {
46794721
// parameter bounds.
46804722
};
46814723

4682-
BoundsExpr *CreateInvalidBoundsExpr();
4683-
/// /brief Synthesize the interop type expression implied by the presence
4684-
/// of a bounds expression. Ty is the original unchecked type. Returns null
4685-
/// if none exists.
4686-
InteropTypeExpr *SynthesizeInteropTypeExpr(QualType Ty, bool IsParam);
4687-
BoundsExpr *CreateCountForArrayType(QualType QT);
4688-
46894724
/// /brief Checks whether an expression is non-modifying
46904725
/// (see Checked C Spec, 3.6.1). Returns true if the expression is non-modifying,
46914726
/// false otherwise.

include/clang/Serialization/ASTBitCodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,7 @@ namespace clang {
14951495
EXPR_INTEROPTYPE_BOUNDS_ANNOTATION,// InteropTypeBoundsAnnotation
14961496
EXPR_POSITIONAL_PARAMETER_EXPR, // PositionalParameterExpr
14971497
EXPR_BOUNDS_CAST,
1498+
EXPR_BOUNDS_VALUE_EXPR, // BoundsValueExpr
14981499
// OpenCL
14991500
EXPR_ASTYPE, // AsTypeExpr
15001501

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ namespace {
619619
void dumpBoundsCheckKind(BoundsCheckKind kind);
620620
void VisitInteropTypeExpr(const InteropTypeExpr *Node);
621621
void VisitPositionalParameterExpr(const PositionalParameterExpr *Node);
622+
void VisitBoundsValueExpr(const BoundsValueExpr *Node);
622623
};
623624
}
624625

@@ -2844,6 +2845,12 @@ void ASTDumper::VisitPositionalParameterExpr(
28442845
OS << Node->getIndex();
28452846
}
28462847

2848+
void ASTDumper::VisitBoundsValueExpr(const BoundsValueExpr *Node) {
2849+
VisitExpr(Node);
2850+
OS << (Node->getKind() == BoundsValueExpr::Kind::Current ?
2851+
" _Current_expr_value" : " _Return_value");
2852+
}
2853+
28472854
//===----------------------------------------------------------------------===//
28482855
// Type method implementations
28492856
//===----------------------------------------------------------------------===//

lib/AST/CanonBounds.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ Result Lexicographic::CompareExpr(const Expr *Arg1, const Expr *Arg2) {
382382
case Expr::InteropTypeExprClass: Cmp = Compare<InteropTypeExpr>(E1, E2); break;
383383
case Expr::PositionalParameterExprClass: Cmp = Compare<PositionalParameterExpr>(E1, E2); break;
384384
case Expr::BoundsCastExprClass: Cmp = Compare<BoundsCastExpr>(E1, E2); break;
385+
case Expr::BoundsValueExprClass: Cmp = Compare<BoundsValueExpr>(E1, E2); break;
385386

386387
// Clang extensions
387388
case Expr::ShuffleVectorExprClass: break;
@@ -744,6 +745,16 @@ Lexicographic::CompareImpl(const BoundsCastExpr *E1,
744745
return CompareType(E1->getType(), E2->getType());
745746
}
746747

748+
Result
749+
Lexicographic::CompareImpl(const BoundsValueExpr *E1,
750+
const BoundsValueExpr *E2) {
751+
Result Cmp = CompareInteger(E1->getKind(), E2->getKind());
752+
if (Cmp != Result::Equal)
753+
return Cmp;
754+
return CompareType(E1->getType(), E2->getType());
755+
}
756+
757+
747758
Result
748759
Lexicographic::CompareImpl(const BlockExpr *E1, const BlockExpr *E2) {
749760
return Result::Equal;

lib/AST/Expr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3066,8 +3066,9 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
30663066
case CountBoundsExprClass:
30673067
case InteropTypeExprClass:
30683068
case NullaryBoundsExprClass:
3069-
case PositionalParameterExprClass:
30703069
case RangeBoundsExprClass:
3070+
case PositionalParameterExprClass:
3071+
case BoundsValueExprClass:
30713072
// These never have a side-effect.
30723073
return false;
30733074

lib/AST/ExprClassification.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
190190
case Expr::ArrayInitIndexExprClass:
191191
case Expr::NoInitExprClass:
192192
case Expr::DesignatedInitUpdateExprClass:
193+
case Expr::BoundsValueExprClass:
193194
return Cl::CL_PRValue;
194195

195196
// Next come the complicated cases.

lib/AST/ExprConstant.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10345,6 +10345,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
1034510345
case Expr::InteropTypeExprClass:
1034610346
case Expr::NullaryBoundsExprClass:
1034710347
case Expr::PositionalParameterExprClass:
10348+
case Expr::BoundsValueExprClass:
1034810349
// These are parameter variables and are never constants,
1034910350
case Expr::RangeBoundsExprClass:
1035010351
return ICEDiag(IK_NotICE, E->getLocStart());

lib/AST/ItaniumMangle.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3478,6 +3478,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
34783478
case Expr::InteropTypeExprClass:
34793479
case Expr::NullaryBoundsExprClass:
34803480
case Expr::RangeBoundsExprClass:
3481+
case Expr::BoundsValueExprClass:
34813482
{
34823483
if (!NullOut) {
34833484
// As bad as this diagnostic is, it's better than crashing.

lib/AST/StmtPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,6 +2082,11 @@ void StmtPrinter::VisitPositionalParameterExpr(PositionalParameterExpr *E) {
20822082
OS << (E->getIndex());
20832083
}
20842084

2085+
void StmtPrinter::VisitBoundsValueExpr(BoundsValueExpr *E) {
2086+
OS << ((E->getKind() == BoundsValueExpr::Current) ?
2087+
"_Current_expr_value" : "_Return_value");
2088+
}
2089+
20852090
// C++
20862091
void StmtPrinter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node) {
20872092
const char *OpStrings[NUM_OVERLOADED_OPERATORS] = {

lib/AST/StmtProfile.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,13 @@ void StmtProfiler::VisitPositionalParameterExpr(
12611261
ID.AddInteger(P->getIndex());
12621262
}
12631263

1264+
void StmtProfiler::VisitBoundsValueExpr(const BoundsValueExpr *S) {
1265+
VisitExpr(S);
1266+
VisitType(S->getType());
1267+
ID.AddInteger(S->getKind());
1268+
}
1269+
1270+
12641271
void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) {
12651272
VisitExpr(S);
12661273
ID.AddInteger(S->getOp());

0 commit comments

Comments
 (0)