Skip to content

Commit 1168760

Browse files
Abel NietoMandeep Singh Grang
authored andcommitted
[WIP] Add existential structs to Checked C (#683)
Cherry-picked from commit e273f31 This large change adds support for existential struct types to Checked C. These can be used to replace uses of void pointers in callbacks, abstract data types, and closures. There is a corresponding commit of tests for existential types to the Checked C repo. This changes adds IR support, parsing support, semantic checking/typechecking support, and code generation support. Serialization/deserialization support still needs to be implemented. There are also a number of cleanups to do, which are documented in GitHub issues. There are three parts to the change: - Support for existential types. - The pack operation. - The unpack operation, which is done as a declaration with an initializer.
1 parent b4b38f7 commit 1168760

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1008
-24
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "clang/AST/TemplateBase.h"
3333
#include "clang/AST/TemplateName.h"
3434
#include "clang/AST/Type.h"
35+
#include "clang/AST/TypeOrdering.h"
3536
#include "clang/Basic/AddressSpaces.h"
3637
#include "clang/Basic/AttrKinds.h"
3738
#include "clang/Basic/IdentifierTable.h"
@@ -295,6 +296,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
295296
/// returns 'true'. The parameters in a type application can be retrieved via RecordDecl::typeParams().
296297
llvm::DenseMap<const RecordDecl *, llvm::SmallVector<RecordDecl *, 4> > DelayedTypeApps;
297298

299+
/// Mapping from a (type-variable, inner-type) pair to the corresponding existential type.
300+
/// e.g. if we've previously created a type E = '_Exists(T, struct Foo<T>)',
301+
/// then, this map links '(T, struct Foo<T>) -> E'.
302+
/// This map alone is not enough to guarantee unique existential types.
303+
/// e.g. consider two types '_Exists(T, struct Foo<T>)' and '_Exists(U, struct Foo<U>)',
304+
/// where 'T = TypeVariableType(0, 0)' and 'U = TypeVariableType(1, 0)' (say, because
305+
/// U shows up at a higher nestedness level). Then the two types will look different
306+
/// when compared with pointer equality, but they should be considered the same type.
307+
/// Another way to say this is to say that this map contains entries that are (functional)
308+
/// duplicates.
309+
llvm::DenseMap<std::pair<const Type *, QualType>, const ExistentialType *> CachedExistTypes;
310+
298311
/// Representation of a "canonical" template template parameter that
299312
/// is used in canonical template names.
300313
class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode {
@@ -3138,6 +3151,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
31383151
/// Remove all type applications that have 'Base' as their base RecordDecl.
31393152
/// Return 'true' if the removed key was in the cache, and 'false' otherwise.
31403153
bool removeDelayedTypeApps(RecordDecl *Base);
3154+
3155+
// Checked C: Existential Types
3156+
3157+
/// Get the existential type corresponding to the pair (type-var, inner-type).
3158+
/// If there is no cached existential, return `nullptr`.
3159+
const ExistentialType *getCachedExistentialType(const Type *TypeVar, QualType InnerType);
3160+
3161+
/// Add the mapping `(type-var, inner-type) -> exist-type` to the cache of
3162+
/// existential types. This should only be called once per key (i.e. cache elements
3163+
/// should not be re-written).
3164+
void addCachedExistentialType(const Type *TypeVar, QualType InnerType, const ExistentialType *ExistType);
31413165
};
31423166

31433167
/// Utility function for constructing a nullary selector.

clang/include/clang/AST/Expr.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,6 +3139,65 @@ class CompoundLiteralExpr : public Expr {
31393139
}
31403140
};
31413141

3142+
/// \brief Represents a Checked C pack expression.
3143+
/// Used like so '_Exists(T, struct Foo<T>) foo = _Pack(fooImpl, _Exists(T, struct Foo<T>), int)'.
3144+
/// A pack expression is a triple (expr, exist_typ, subst), indicating that we type
3145+
/// 'expr' with the existential type 'exist_typ', using the substitution 'T -> subst', where
3146+
/// 'T' is the type variable bound by the existential type.
3147+
/// e.g. in the example above, 'fooImpl' must have type 'struct Foo<int>', which we obtain by
3148+
/// substituting 'int' for 'T' in 'struct Foo<T>'.
3149+
class PackExpr : public Expr {
3150+
private:
3151+
/// Start and end location for the pack expression.
3152+
SourceLocation StartLoc, EndLoc;
3153+
3154+
/// The expression that is being packed.
3155+
Expr *PackedExpr = nullptr;
3156+
3157+
/// The result type of the pack operation. This is always an existential type
3158+
/// after unwrapping the QualType.
3159+
QualType ExistType;
3160+
/// The type that will be substituted for the variable in the existential.
3161+
/// We can combine the existential and this type to check type of the expression
3162+
/// being packed.
3163+
/// e.g. if we do '_Pack(foo, _Exists(T, struct Foo<T, U, T>), int)', then
3164+
/// 'foo' must have type 'struct Foo<int, U, int>'.
3165+
QualType Subst;
3166+
3167+
public:
3168+
PackExpr(Expr *PackedExpr, QualType ExistType, QualType Subst, SourceLocation StartLoc, SourceLocation EndLoc) :
3169+
Expr(PackExprClass, ExistType, VK_RValue, OK_Ordinary, false, false, false, false),
3170+
StartLoc(StartLoc), EndLoc(EndLoc), PackedExpr(PackedExpr), ExistType(ExistType), Subst(Subst) {
3171+
if(!ExistType->isExistentialType()) {
3172+
llvm_unreachable("_Pack expression expects an existential type");
3173+
}
3174+
}
3175+
3176+
/// Return the expression packed by this pack expression.
3177+
Expr *getPackedExpr() const { return PackedExpr; }
3178+
3179+
/// Return the type of the pack expression, which must be an existential (after unwrapping).
3180+
QualType getExistentialType() const { return ExistType; }
3181+
3182+
/// Return the "substitution" type that is used to verify that
3183+
/// the underlying expr can be packed into the existential type.
3184+
QualType getSubst() const { return Subst; }
3185+
3186+
SourceLocation getBeginLoc() const LLVM_READONLY { return StartLoc; }
3187+
SourceLocation getEndLoc() const LLVM_READONLY { return EndLoc; }
3188+
3189+
child_range children() {
3190+
// Pretend the children are stored in a one-element array.
3191+
Stmt *Child = PackedExpr;
3192+
return child_range(&Child, &Child + 1);
3193+
}
3194+
const_child_range children() const {
3195+
// Pretend the children are stored in a one-element array.
3196+
Stmt *Child = PackedExpr;
3197+
return const_child_range(&Child, &Child + 1);
3198+
}
3199+
};
3200+
31423201
/// \brief Represents a Checked C bounds expression.
31433202
class BoundsExpr : public Expr {
31443203
private:

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,11 @@ DEF_TRAVERSE_TYPE(UnresolvedUsingType, {})
10261026
DEF_TRAVERSE_TYPE(TypedefType, {})
10271027
DEF_TRAVERSE_TYPE(TypeVariableType, {})
10281028

1029+
DEF_TRAVERSE_TYPE(ExistentialType, {
1030+
TRY_TO(TraverseType(QualType(T->typeVar(), 0 /* Quals */)));
1031+
TRY_TO(TraverseType(T->innerType()));
1032+
})
1033+
10291034
DEF_TRAVERSE_TYPE(TypeOfExprType,
10301035
{ TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
10311036

@@ -1263,6 +1268,7 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
12631268
DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {})
12641269
DEF_TRAVERSE_TYPELOC(TypedefType, {})
12651270
DEF_TRAVERSE_TYPELOC(TypeVariableType, {})
1271+
DEF_TRAVERSE_TYPELOC(ExistentialType, {}) // TODO: is this correct? (checkedc issue #661)
12661272

12671273
DEF_TRAVERSE_TYPELOC(TypeOfExprType,
12681274
{ TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
@@ -2579,6 +2585,7 @@ DEF_TRAVERSE_STMT(InteropTypeExpr, {})
25792585
DEF_TRAVERSE_STMT(PositionalParameterExpr, {})
25802586
DEF_TRAVERSE_STMT(BoundsValueExpr, {})
25812587
DEF_TRAVERSE_STMT(CHKCBindTemporaryExpr, {})
2588+
DEF_TRAVERSE_STMT(PackExpr, {})
25822589

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

clang/include/clang/AST/Type.h

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2063,6 +2063,7 @@ class Type : public ExtQualsTypeCommonBase {
20632063
bool isNtCheckedArrayType() const;
20642064
bool isUncheckedArrayType() const;
20652065
bool isRecordType() const;
2066+
bool isExistentialType() const; // Checked C existential type
20662067
bool isClassType() const;
20672068
bool isStructureType() const;
20682069
bool isObjCBoxableRecordType() const;
@@ -4403,7 +4404,7 @@ class TypeVariableType : public Type, public llvm::FoldingSetNode {
44034404
return isBoundsInterfaceType;
44044405
}
44054406

4406-
void Profile(llvm::FoldingSetNodeID &ID) {
4407+
void Profile(llvm::FoldingSetNodeID &ID) const {
44074408
Profile(ID, depth, index, isBoundsInterfaceType);
44084409
}
44094410
static void Profile(llvm::FoldingSetNodeID &ID, unsigned int inDepth, unsigned int inIndex,
@@ -4416,7 +4417,6 @@ class TypeVariableType : public Type, public llvm::FoldingSetNode {
44164417
static bool classof(const Type *T) { return T->getTypeClass() == TypeVariable; }
44174418
};
44184419

4419-
44204420
class TypedefType : public Type {
44214421
TypedefNameDecl *Decl;
44224422

@@ -4441,6 +4441,57 @@ class TypedefType : public Type {
44414441
static bool classof(const Type *T) { return T->getTypeClass() == Typedef; }
44424442
};
44434443

4444+
/// (Checked C extension)
4445+
/// Represents the type '_Exists(T, InnerType)', where 'T' is a type variable and
4446+
/// 'InnerType' is some type that potentially uses 'T'.
4447+
/// There are some restrictions on what 'InnerType' can be. These restrictions are enforced
4448+
/// via checks in the 'pack' and 'unpack' operations, but they amount to requiring that
4449+
/// 'InnerType' is one of:
4450+
/// 1) another existential type: e.g. '_Exists(T, _Exists(U, struct Foo<T, U>))'
4451+
/// 2) a type application 'C<T>', where 'C' is a generic struct: e.g. '_Exists(T, struct List<T>)'
4452+
/// 3) a pointer that satisfies one of 1) - 3)
4453+
class ExistentialType : public Type, public llvm::FoldingSetNode {
4454+
/// The type variable that is bound by the existential.
4455+
/// This is of type 'Type' to allow for some polymorphism:
4456+
/// non-canonical existential types will contain a 'TypedefType' here,
4457+
/// while canonical ones will contain the underlying 'TypeVariableType'.
4458+
/// This isn't a 'QualType' because the variable bound by an existential cannot be
4459+
/// qualified.
4460+
const Type *TypeVar = nullptr;
4461+
/// The type wrapped by the existential that potentially uses the bound type variable.
4462+
QualType InnerType;
4463+
4464+
public:
4465+
ExistentialType(const Type *TypeVar, QualType InnerType, QualType Canon) :
4466+
Type(Existential, Canon, false /* Dependent */ , false /* InstantiationDependent */,
4467+
false /* VariablyModified */, false /* ContainsUnexpandedParameterPack */),
4468+
TypeVar(TypeVar), InnerType(InnerType) {
4469+
if (!TypedefType::classof(TypeVar) && !TypeVariableType::classof(TypeVar)) {
4470+
llvm_unreachable("Type variable should be a 'TypedefType' or 'TypeVariableType'");
4471+
}
4472+
}
4473+
4474+
const Type *typeVar() const { return TypeVar; }
4475+
QualType innerType() const { return InnerType; }
4476+
4477+
bool isSugared(void) const { return false; }
4478+
QualType desugar(void) const { return QualType(this, 0); }
4479+
4480+
// TODO: can't implement 'Profile' because TypedefType doesn't have a 'Profile' method (checkedc issue #661).
4481+
/*
4482+
void Profile(llvm::FoldingSetNodeID &ID) {
4483+
Profile(ID, TypeVar, InnerType);
4484+
}
4485+
static void Profile(llvm::FoldingSetNodeID &ID, const Type *TypeVar, QualType InnerType) {
4486+
if (const auto *TV = TypeVar->getAs<TypedefType>()) TV->Profile(ID);
4487+
else if (const auto *TV = TypeVar->getAs<TypeVariableType>()) TV->Profile(ID);
4488+
InnerType.Profile(ID);
4489+
}
4490+
*/
4491+
4492+
static bool classof(const Type *T) { return T->getTypeClass() == Existential; }
4493+
};
4494+
44444495
/// Represents a `typeof` (or __typeof__) expression (a GCC extension).
44454496
class TypeOfExprType : public Type {
44464497
Expr *TOExpr;
@@ -6713,6 +6764,10 @@ inline bool Type::isRecordType() const {
67136764
return isa<RecordType>(CanonicalType);
67146765
}
67156766

6767+
inline bool Type::isExistentialType() const {
6768+
return isa<ExistentialType>(CanonicalType);
6769+
}
6770+
67166771
inline bool Type::isEnumeralType() const {
67176772
return isa<EnumType>(CanonicalType);
67186773
}

clang/include/clang/AST/TypeLoc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,11 @@ class TypeVariableTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
666666

667667
};
668668

669+
class ExistentialTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
670+
ExistentialTypeLoc,
671+
ExistentialType> {
672+
};
673+
669674
/// Wrapper for source info for typedefs.
670675

671676
class TypedefTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,

clang/include/clang/AST/TypeNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ ABSTRACT_TYPE(Reference, Type)
6767
TYPE(LValueReference, ReferenceType)
6868
TYPE(RValueReference, ReferenceType)
6969
TYPE(TypeVariable, Type)
70+
TYPE(Existential, Type)
7071
TYPE(MemberPointer, Type)
7172
ABSTRACT_TYPE(Array, Type)
7273
TYPE(ConstantArray, ArrayType)

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ def err_single_itype_expr_allowed : Error<
12881288
def err_forany_type_variable_declaration_expected : Error<
12891289
"expected list of type variables">;
12901290

1291-
def err_forany_type_variable_identifier_expected : Error<
1291+
def err_type_variable_identifier_expected : Error<
12921292
"expected type variable identifier">;
12931293

12941294
def err_generic_specifier_unexpected_token : Error<
@@ -1312,6 +1312,15 @@ def err_expected_type_argument_list_for_generic_instance : Error<
13121312
def err_expected_type_argument_list_for_itype_generic_instance : Error<
13131313
"expected a type argument list for a struct with a bounds-safe interface in a checked scope">;
13141314

1315+
def err_pack_expr_returns_existential_type : Error <
1316+
"return type of a pack expression must be an existential type, but got %0 instead">;
1317+
1318+
def err_unpack_expected_one_type_variable : Error<
1319+
"unpack specifier expects exactly one type variable, but got %0 instead">;
1320+
1321+
def err_unpack_expected_existential_initializer : Error<
1322+
"unpack specifer expects an initializer that has an existential type">;
1323+
13151324
// Checked C pragmas
13161325
def err_pragma_checked_scope_invalid_argument : Error<
13171326
"unexpected argument '%0' to '#pragma CHECKED_SCOPE'; "

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9659,6 +9659,9 @@ def err_typecheck_bounds_type_annotation_incompatible : Error<
96599659
def err_typecheck_bounds_type_annotation_must_be_checked_type : Error<
96609660
"type must be a checked type">;
96619661

9662+
def err_typecheck_existential_type_witness_mismatch : Error<
9663+
"witness type does not match existential type">;
9664+
96629665
def err_bounds_type_annotation_lost_checking : Error<
96639666
"type %diff{$ loses checking of declared type $|"
96649667
"loses checking of declared type}0,1">;

clang/include/clang/Basic/Specifiers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ namespace clang {
8181
TST_plainPtr, // Checked C _Ptr type
8282
TST_arrayPtr, // Checked C _Array_ptr type
8383
TST_ntarrayPtr , // Chcecked C _Nt_array_ptr type
84+
TST_exists, // Checked C _Exists type
8485
#define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
8586
#include "clang/Basic/OpenCLImageTypes.def"
8687
TST_error // erroneous type

clang/include/clang/Basic/StmtNodes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ def PositionalParameterExpr : DStmt<Expr>;
190190
def BoundsCastExpr : DStmt<ExplicitCastExpr>;
191191
def CHKCBindTemporaryExpr : DStmt<Expr>;
192192
def BoundsValueExpr : DStmt<Expr>;
193+
def PackExpr : DStmt<Expr>;
193194

194195
// CUDA Expressions.
195196
def CUDAKernelCallExpr : DStmt<CallExpr>;

0 commit comments

Comments
 (0)