Skip to content

[WIP] Add existential structs to Checked C #683

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 86 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
78bb3c2
Add a new existential type to the type hierarchy
Aug 10, 2019
aeae6b2
Handle existential types in different places
Aug 13, 2019
7f958ed
More handling of new ExistentialType
Aug 14, 2019
6aeb71e
Add new _Exists token
Aug 14, 2019
4163a7a
Handle existentials in a few more places
Aug 14, 2019
5372b6a
Handle existentials in additional places (including codegen)
Aug 14, 2019
f9ea757
[wip] Add an existential type to clang
Aug 14, 2019
e43194b
Desugar type variable in existential
Aug 14, 2019
b37ab7a
Fix bug in desugaring of exists
Aug 15, 2019
75c9c4c
Add dedicated scopes for existentials
Aug 15, 2019
413f16d
Rename some variables
Aug 15, 2019
0b01466
[wip] Instantiate existential types
Aug 16, 2019
fa606a4
Fix bugs
Aug 16, 2019
405fa0e
Better logic for determining whether an Existential is an aggregate
Aug 16, 2019
578203c
Disambiguate between expressions and decls in the parser
Aug 16, 2019
53a0f15
[wip] add a pack operation and start handling it all over the compiler
Aug 21, 2019
86aa6b2
Add support for pack in additional locations
Aug 21, 2019
b0b9b3f
Hook up pack operation in parser
Aug 21, 2019
8cbd3d8
[wip] parse pack expressions and generate ast node
Aug 21, 2019
7983ede
Can now parse pack expressions
Aug 21, 2019
8f4ff68
Codegen pack expressions
Aug 21, 2019
354cc6d
Slightly better merge function for existential types
Aug 21, 2019
b1e574d
Better caching of type applications and existentials
Aug 22, 2019
2f341be
Fix typo in comment
Aug 22, 2019
5b87c35
Check witness type in pack expressions
Aug 23, 2019
8c46da6
Add pack expression in the right place
Aug 26, 2019
1f6030d
[wip] parse unpack type specifiers
Aug 26, 2019
7c8e5e2
[wip] parse unpack specifiers
Aug 27, 2019
e5675cd
Move unpack to custom specifier
Aug 27, 2019
7690cb6
Differentiate between unpack variables in same scope
Aug 27, 2019
446b329
Add hacky rule for typing unpack init
Aug 28, 2019
d2452d8
bug fix
Sep 3, 2019
c6fe682
Minor fixes
Sep 3, 2019
bc84288
Restructure ExistentialType to contain more than a TypeVariableType f…
Sep 3, 2019
242988d
Fix evaluation kind resolution
Sep 4, 2019
4d8c235
Better canonical types for existentials
Sep 4, 2019
42ddb2a
Simplifying generation of canonical types
Sep 4, 2019
a546376
Fix bug in creating existential type
Sep 4, 2019
5b7ebc8
Revert changes to numbering of existential type variables
Sep 4, 2019
e34fd77
Fix bug in allocation logic
Sep 4, 2019
e531285
Implement a visitor that computes free variables
Sep 6, 2019
cbeadfa
Comment out logging code
Sep 6, 2019
b3f0c2e
Implement alpha renamer and wip canonical type generation
Sep 6, 2019
f669735
Hooked up canonicalization to creation of existential types
Sep 9, 2019
3267393
Fix bugs
Sep 9, 2019
3deb548
handle typedefs in alpha renamer
Sep 9, 2019
00306ea
handle type applications
Sep 9, 2019
f2eb814
fix bug
Sep 9, 2019
dc545f0
fix another bug
Sep 10, 2019
49fba21
remoev incorrect canonicalization
Sep 10, 2019
fd3bccc
fix bug in insertion to subst map
Sep 10, 2019
186570e
fix bug in checking witness type
Sep 10, 2019
cc5e1a0
Add TODO
Sep 11, 2019
629d1e3
fix codegen of type applications
Sep 11, 2019
9673f22
Remove unused static variable
Sep 17, 2019
a30a6bf
Emit parsing error while parsing existentials
Sep 17, 2019
3ff8f48
Improve wording
Sep 17, 2019
e1f0e38
Rename ExistTpe to ExistType
Sep 17, 2019
bc9d188
Improve wording
Sep 17, 2019
bd21568
Improve wording
Sep 17, 2019
007903f
Add issue number to TODOs
Sep 17, 2019
f23ff53
Improve wording
Sep 17, 2019
357545f
Add explanation
Sep 17, 2019
41f9283
Fix typo
Sep 17, 2019
92dce16
Check return type of pack expressions (must be an existential)
Sep 17, 2019
be767b8
Strengthen assertion
Sep 17, 2019
79ee960
Tag comments with issue number
Sep 17, 2019
50b0d76
Tag comment
Sep 17, 2019
a637a0c
Improve wording
Sep 17, 2019
9022551
Replace assertion by runtime error message
Sep 18, 2019
50cac91
Check that an unpack initializer has an existential type
Sep 18, 2019
4aa5032
Add comment about pack expressions being r-values
Sep 18, 2019
9c8fcfc
Remove unneeded comment
Sep 18, 2019
2645d62
Compute type linkage info in different way
Sep 18, 2019
298162c
Tag comment
Sep 18, 2019
485b99e
Improve comment
Sep 18, 2019
0d4b28e
Tag comment
Sep 18, 2019
766f068
Check for malformed unpacks
Sep 18, 2019
c0403de
Fix warning and missing symbol
Sep 19, 2019
07102de
Add missing switch case
Sep 19, 2019
de74377
Partially disable clang test
Sep 19, 2019
40b584c
Fix codegen check
Sep 19, 2019
02c5dc3
Fix typo
Sep 19, 2019
13774ae
Improve handling of linkage for existentials
Sep 19, 2019
c10b52a
Use getAs() instead of manually getting canonical type
Sep 19, 2019
3d0caff
Address code review comment
Sep 19, 2019
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
24 changes: 24 additions & 0 deletions include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -295,6 +296,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// returns 'true'. The parameters in a type application can be retrieved via RecordDecl::typeParams().
llvm::DenseMap<const RecordDecl *, llvm::SmallVector<RecordDecl *, 4> > DelayedTypeApps;

/// Mapping from a (type-variable, inner-type) pair to the corresponding existential type.
/// e.g. if we've previously created a type E = '_Exists(T, struct Foo<T>)',
/// then, this map links '(T, struct Foo<T>) -> E'.
/// This map alone is not enough to guarantee unique existential types.
/// e.g. consider two types '_Exists(T, struct Foo<T>)' and '_Exists(U, struct Foo<U>)',
/// where 'T = TypeVariableType(0, 0)' and 'U = TypeVariableType(1, 0)' (say, because
/// U shows up at a higher nestedness level). Then the two types will look different
/// when compared with pointer equality, but they should be considered the same type.
/// Another way to say this is to say that this map contains entries that are (functional)
/// duplicates.
llvm::DenseMap<std::pair<const Type *, QualType>, const ExistentialType *> CachedExistTypes;

/// Representation of a "canonical" template template parameter that
/// is used in canonical template names.
class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode {
Expand Down Expand Up @@ -3138,6 +3151,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Remove all type applications that have 'Base' as their base RecordDecl.
/// Return 'true' if the removed key was in the cache, and 'false' otherwise.
bool removeDelayedTypeApps(RecordDecl *Base);

// Checked C: Existential Types

/// Get the existential type corresponding to the pair (type-var, inner-type).
/// If there is no cached existential, return `nullptr`.
const ExistentialType *getCachedExistentialType(const Type *TypeVar, QualType InnerType);

/// Add the mapping `(type-var, inner-type) -> exist-type` to the cache of
/// existential types. This should only be called once per key (i.e. cache elements
/// should not be re-written).
void addCachedExistentialType(const Type *TypeVar, QualType InnerType, const ExistentialType *ExistType);
};

/// Utility function for constructing a nullary selector.
Expand Down
59 changes: 59 additions & 0 deletions include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3139,6 +3139,65 @@ class CompoundLiteralExpr : public Expr {
}
};

/// \brief Represents a Checked C pack expression.
/// Used like so '_Exists(T, struct Foo<T>) foo = _Pack(fooImpl, _Exists(T, struct Foo<T>), int)'.
/// A pack expression is a triple (expr, exist_typ, subst), indicating that we type
/// 'expr' with the existential type 'exist_typ', using the substitution 'T -> subst', where
/// 'T' is the type variable bound by the existential type.
/// e.g. in the example above, 'fooImpl' must have type 'struct Foo<int>', which we obtain by
/// substituting 'int' for 'T' in 'struct Foo<T>'.
class PackExpr : public Expr {
private:
/// Start and end location for the pack expression.
SourceLocation StartLoc, EndLoc;

/// The expression that is being packed.
Expr *PackedExpr = nullptr;

/// The result type of the pack operation. This is always an existential type
/// after unwrapping the QualType.
QualType ExistType;
/// The type that will be substituted for the variable in the existential.
/// We can combine the existential and this type to check type of the expression
/// being packed.
/// e.g. if we do '_Pack(foo, _Exists(T, struct Foo<T, U, T>), int)', then
/// 'foo' must have type 'struct Foo<int, U, int>'.
QualType Subst;

public:
PackExpr(Expr *PackedExpr, QualType ExistType, QualType Subst, SourceLocation StartLoc, SourceLocation EndLoc) :
Expr(PackExprClass, ExistType, VK_RValue, OK_Ordinary, false, false, false, false),
StartLoc(StartLoc), EndLoc(EndLoc), PackedExpr(PackedExpr), ExistType(ExistType), Subst(Subst) {
if(!ExistType->isExistentialType()) {
llvm_unreachable("_Pack expression expects an existential type");
}
}

/// Return the expression packed by this pack expression.
Expr *getPackedExpr() const { return PackedExpr; }

/// Return the type of the pack expression, which must be an existential (after unwrapping).
QualType getExistentialType() const { return ExistType; }

/// Return the "substitution" type that is used to verify that
/// the underlying expr can be packed into the existential type.
QualType getSubst() const { return Subst; }

SourceLocation getBeginLoc() const LLVM_READONLY { return StartLoc; }
SourceLocation getEndLoc() const LLVM_READONLY { return EndLoc; }

child_range children() {
// Pretend the children are stored in a one-element array.
Stmt *Child = PackedExpr;
return child_range(&Child, &Child + 1);
}
const_child_range children() const {
// Pretend the children are stored in a one-element array.
Stmt *Child = PackedExpr;
return const_child_range(&Child, &Child + 1);
}
};

/// \brief Represents a Checked C bounds expression.
class BoundsExpr : public Expr {
private:
Expand Down
7 changes: 7 additions & 0 deletions include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,11 @@ DEF_TRAVERSE_TYPE(UnresolvedUsingType, {})
DEF_TRAVERSE_TYPE(TypedefType, {})
DEF_TRAVERSE_TYPE(TypeVariableType, {})

DEF_TRAVERSE_TYPE(ExistentialType, {
TRY_TO(TraverseType(QualType(T->typeVar(), 0 /* Quals */)));
TRY_TO(TraverseType(T->innerType()));
})

DEF_TRAVERSE_TYPE(TypeOfExprType,
{ TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })

Expand Down Expand Up @@ -1263,6 +1268,7 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {})
DEF_TRAVERSE_TYPELOC(TypedefType, {})
DEF_TRAVERSE_TYPELOC(TypeVariableType, {})
DEF_TRAVERSE_TYPELOC(ExistentialType, {}) // TODO: is this correct? (checkedc issue #661)

DEF_TRAVERSE_TYPELOC(TypeOfExprType,
{ TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
Expand Down Expand Up @@ -2579,6 +2585,7 @@ DEF_TRAVERSE_STMT(InteropTypeExpr, {})
DEF_TRAVERSE_STMT(PositionalParameterExpr, {})
DEF_TRAVERSE_STMT(BoundsValueExpr, {})
DEF_TRAVERSE_STMT(CHKCBindTemporaryExpr, {})
DEF_TRAVERSE_STMT(PackExpr, {})

// For coroutines expressions, traverse either the operand
// as written or the implied calls, depending on what the
Expand Down
59 changes: 57 additions & 2 deletions include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,7 @@ class Type : public ExtQualsTypeCommonBase {
bool isNtCheckedArrayType() const;
bool isUncheckedArrayType() const;
bool isRecordType() const;
bool isExistentialType() const; // Checked C existential type
bool isClassType() const;
bool isStructureType() const;
bool isObjCBoxableRecordType() const;
Expand Down Expand Up @@ -4403,7 +4404,7 @@ class TypeVariableType : public Type, public llvm::FoldingSetNode {
return isBoundsInterfaceType;
}

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


class TypedefType : public Type {
TypedefNameDecl *Decl;

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

/// (Checked C extension)
/// Represents the type '_Exists(T, InnerType)', where 'T' is a type variable and
/// 'InnerType' is some type that potentially uses 'T'.
/// There are some restrictions on what 'InnerType' can be. These restrictions are enforced
/// via checks in the 'pack' and 'unpack' operations, but they amount to requiring that
/// 'InnerType' is one of:
/// 1) another existential type: e.g. '_Exists(T, _Exists(U, struct Foo<T, U>))'
/// 2) a type application 'C<T>', where 'C' is a generic struct: e.g. '_Exists(T, struct List<T>)'
/// 3) a pointer that satisfies one of 1) - 3)
class ExistentialType : public Type, public llvm::FoldingSetNode {
/// The type variable that is bound by the existential.
/// This is of type 'Type' to allow for some polymorphism:
/// non-canonical existential types will contain a 'TypedefType' here,
/// while canonical ones will contain the underlying 'TypeVariableType'.
/// This isn't a 'QualType' because the variable bound by an existential cannot be
/// qualified.
const Type *TypeVar = nullptr;
/// The type wrapped by the existential that potentially uses the bound type variable.
QualType InnerType;

public:
ExistentialType(const Type *TypeVar, QualType InnerType, QualType Canon) :
Type(Existential, Canon, false /* Dependent */ , false /* InstantiationDependent */,
false /* VariablyModified */, false /* ContainsUnexpandedParameterPack */),
TypeVar(TypeVar), InnerType(InnerType) {
if (!TypedefType::classof(TypeVar) && !TypeVariableType::classof(TypeVar)) {
llvm_unreachable("Type variable should be a 'TypedefType' or 'TypeVariableType'");
}
}

const Type *typeVar() const { return TypeVar; }
QualType innerType() const { return InnerType; }

bool isSugared(void) const { return false; }
QualType desugar(void) const { return QualType(this, 0); }

// TODO: can't implement 'Profile' because TypedefType doesn't have a 'Profile' method (checkedc issue #661).
/*
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, TypeVar, InnerType);
}
static void Profile(llvm::FoldingSetNodeID &ID, const Type *TypeVar, QualType InnerType) {
if (const auto *TV = TypeVar->getAs<TypedefType>()) TV->Profile(ID);
else if (const auto *TV = TypeVar->getAs<TypeVariableType>()) TV->Profile(ID);
InnerType.Profile(ID);
}
*/

static bool classof(const Type *T) { return T->getTypeClass() == Existential; }
};

/// Represents a `typeof` (or __typeof__) expression (a GCC extension).
class TypeOfExprType : public Type {
Expr *TOExpr;
Expand Down Expand Up @@ -6713,6 +6764,10 @@ inline bool Type::isRecordType() const {
return isa<RecordType>(CanonicalType);
}

inline bool Type::isExistentialType() const {
return isa<ExistentialType>(CanonicalType);
}

inline bool Type::isEnumeralType() const {
return isa<EnumType>(CanonicalType);
}
Expand Down
5 changes: 5 additions & 0 deletions include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,11 @@ class TypeVariableTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,

};

class ExistentialTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
ExistentialTypeLoc,
ExistentialType> {
};

/// Wrapper for source info for typedefs.

class TypedefTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
Expand Down
1 change: 1 addition & 0 deletions include/clang/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ ABSTRACT_TYPE(Reference, Type)
TYPE(LValueReference, ReferenceType)
TYPE(RValueReference, ReferenceType)
TYPE(TypeVariable, Type)
TYPE(Existential, Type)
TYPE(MemberPointer, Type)
ABSTRACT_TYPE(Array, Type)
TYPE(ConstantArray, ArrayType)
Expand Down
11 changes: 10 additions & 1 deletion include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ def err_single_itype_expr_allowed : Error<
def err_forany_type_variable_declaration_expected : Error<
"expected list of type variables">;

def err_forany_type_variable_identifier_expected : Error<
def err_type_variable_identifier_expected : Error<
"expected type variable identifier">;

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

def err_pack_expr_returns_existential_type : Error <
"return type of a pack expression must be an existential type, but got %0 instead">;

def err_unpack_expected_one_type_variable : Error<
"unpack specifier expects exactly one type variable, but got %0 instead">;

def err_unpack_expected_existential_initializer : Error<
"unpack specifer expects an initializer that has an existential type">;

// Checked C pragmas
def err_pragma_checked_scope_invalid_argument : Error<
"unexpected argument '%0' to '#pragma CHECKED_SCOPE'; "
Expand Down
3 changes: 3 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9659,6 +9659,9 @@ def err_typecheck_bounds_type_annotation_incompatible : Error<
def err_typecheck_bounds_type_annotation_must_be_checked_type : Error<
"type must be a checked type">;

def err_typecheck_existential_type_witness_mismatch : Error<
"witness type does not match existential type">;

def err_bounds_type_annotation_lost_checking : Error<
"type %diff{$ loses checking of declared type $|"
"loses checking of declared type}0,1">;
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/Specifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace clang {
TST_plainPtr, // Checked C _Ptr type
TST_arrayPtr, // Checked C _Array_ptr type
TST_ntarrayPtr , // Chcecked C _Nt_array_ptr type
TST_exists, // Checked C _Exists type
#define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
#include "clang/Basic/OpenCLImageTypes.def"
TST_error // erroneous type
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def PositionalParameterExpr : DStmt<Expr>;
def BoundsCastExpr : DStmt<ExplicitCastExpr>;
def CHKCBindTemporaryExpr : DStmt<Expr>;
def BoundsValueExpr : DStmt<Expr>;
def PackExpr : DStmt<Expr>;

// CUDA Expressions.
def CUDAKernelCallExpr : DStmt<CallExpr>;
Expand Down
3 changes: 3 additions & 0 deletions include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,9 @@ KEYWORD(_Itype_for_any , KEYCHECKEDC)
KEYWORD(_Current_expr_value , KEYCHECKEDC)
KEYWORD(_Return_value , KEYCHECKEDC)
KEYWORD(_Bounds_only , KEYCHECKEDC)
KEYWORD(_Exists , KEYCHECKEDC)
KEYWORD(_Pack , KEYCHECKEDC)
KEYWORD(_Unpack , KEYCHECKEDC)

// Borland Extensions which should be disabled in strict conformance mode.
ALIAS("_pascal" , __pascal , KEYBORLAND)
Expand Down
6 changes: 6 additions & 0 deletions include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,9 @@ class Parser : public CodeCompletionHandler {

DeclResult ParseRecordTypeApplication(RecordDecl *Base, bool IsItypeGeneric);

/// Parse a pack expression of the form '_Pack(expr, existential_type, substitution_type)'.
ExprResult ParsePackExpression();

//===--------------------------------------------------------------------===//
// clang Expressions

Expand Down Expand Up @@ -2646,6 +2649,9 @@ class Parser : public CodeCompletionHandler {
void ParseUnderlyingTypeSpecifier(DeclSpec &DS);
void ParseAtomicSpecifier(DeclSpec &DS);
void ParseCheckedPointerSpecifiers(DeclSpec & DS);
void ParseExistentialTypeSpecifier(DeclSpec &DS);
void ParseUnpackSpecifier(DeclSpec &DS);
void ParseExistentialTypeSpecifierHelper(DeclSpec &DS);
void ParseForanySpecifier(DeclSpec &DS);
bool ParseForanySpecifierHelper(DeclSpec &DS, Scope::ScopeFlags S);
void ParseItypeforanySpecifier(DeclSpec &DS);
Expand Down
Loading