Skip to content

Revamp implementation of generic functions. #581

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 10 commits into from
Oct 29, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Refactor code.
Type substitution done at type applications is buggy.  Refactor
code before fixing it.  The fix will require access to the AST
tree writing support in TreeTransform.h.

- Move type substitution from ASTContext to the Sema directory.
  Add a new file CheckedCSubst.cpp to hold it.
- Move semantic checking for type applications from Parse
  to Sema.  Place a new function for this in CheckedCSubst.cpp.
  This is where it should have been to start with.

Testing:
- Passes existing tests, with failures due to issues in type
  substitution.
  • Loading branch information
dtarditi committed Oct 15, 2018
commit 9b52f85dd72f8cdf71feedf923f09a4b53e8e940
3 changes: 0 additions & 3 deletions include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2583,9 +2583,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// if the pointer is null.
QualType getInteropTypeAndAdjust(const InteropTypeExpr *BA, bool IsParam) const;

QualType substituteTypeArgs(QualType QT,
SmallVector<DeclRefExpr::GenericInstInfo::TypeArgument, 4> &typeNames);

//===--------------------------------------------------------------------===//
// Integer Predicates
//===--------------------------------------------------------------------===//
Expand Down
9 changes: 0 additions & 9 deletions include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1210,15 +1210,6 @@ def err_expected_list_of_types_expr_for_generic_function : Error<
def err_type_function_comma_or_greater_expected : Error<
"expected , or >">;

def err_type_list_and_type_variable_num_mismatch : Error<
"mismatch between the number of types listed and number of type variables">;

def note_type_variables_declared_at : Note<
"type variable(s) declared here">;

def err_type_args_for_non_generic_expression : Error<
"type arguments supplied for non-generic function or expression">;

// Checked C pragmas
def err_pragma_checked_scope_invalid_argument : Error<
"unexpected argument '%0' to '#pragma CHECKED_SCOPE'; "
Expand Down
12 changes: 12 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9812,6 +9812,18 @@ def err_bounds_type_annotation_lost_checking : Error<
def err_expected_type_argument_list_for_itype_generic_call : Error<
"expected a type argument list for a bounds-safe interface call in a checked scope">;

def err_type_list_and_type_variable_num_mismatch : Error<
"mismatch between the number of types listed and number of type variables">;

def note_type_variables_declared_at : Note<
"type variable(s) declared here">;

def err_type_args_for_non_generic_expression : Error<
"type arguments supplied for non-generic function or expression">;

def err_type_args_limited : Error<
"type arguments only supported for variables and function names right now">;

} // end of Checked C Category

} // end of sema component.
2 changes: 1 addition & 1 deletion include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1749,7 +1749,7 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseBoundsCastExpression();

ExprResult ParseBoundsExpression();
bool ParseGenericTypeArgumentList(ExprResult &Res, SourceLocation Loc);
ExprResult ParseGenericTypeArgumentList(ExprResult TypeFunc, SourceLocation Loc);

QualType SubstituteTypeVariable(QualType QT,
SmallVector<DeclRefExpr::GenericInstInfo::TypeArgument, 4> &typeNames);
Expand Down
6 changes: 6 additions & 0 deletions include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4809,6 +4809,12 @@ class Sema {

BoundsExpr *CheckNonModifyingBounds(BoundsExpr *Bounds, Expr *E);

ExprResult ActOnTypeApplication(ExprResult TypeFunc, SourceLocation Loc,
ArrayRef<DeclRefExpr::GenericInstInfo::TypeArgument> Args);

QualType SubstituteTypeArgs(QualType QT,
ArrayRef<DeclRefExpr::GenericInstInfo::TypeArgument> TypeArgs);

bool AbstractForFunctionType(BoundsAnnotations &BA,
ArrayRef<DeclaratorChunk::ParamInfo> Params);
/// \brief Take a bounds expression with positional parameters from a function
Expand Down
188 changes: 0 additions & 188 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9227,194 +9227,6 @@ void ASTContext::addMemberBoundsUse(const MemberPath &Path,
UsingBounds[Path].push_back(Bounds);
}

// Recurse through types and substitute any TypedefType with TypeVariableType
// as the underlying type.
QualType ASTContext::substituteTypeArgs(QualType QT,
SmallVector<DeclRefExpr::GenericInstInfo::TypeArgument, 4> &typeNames) {
const Type *T = QT.getTypePtr();
switch (T->getTypeClass()) {
case Type::Pointer: {
const PointerType *pType = cast<PointerType>(T);
QualType newPointee = substituteTypeArgs(pType->getPointeeType(), typeNames);
return getPointerType(newPointee, pType->getKind());
}
case Type::ConstantArray: {
const ConstantArrayType *caType = cast<ConstantArrayType>(T);
QualType EltTy = substituteTypeArgs(caType->getElementType(), typeNames);
const llvm::APInt ArySize = caType->getSize();
ArrayType::ArraySizeModifier ASM = caType->getSizeModifier();
unsigned IndexTypeQuals = caType->getIndexTypeCVRQualifiers();
CheckedArrayKind Kind = caType->getKind();
return getConstantArrayType(EltTy, ArySize, ASM, IndexTypeQuals, Kind);
}
case Type::VariableArray: {
const VariableArrayType *vaType = cast<VariableArrayType>(T);
QualType EltTy = substituteTypeArgs(vaType->getElementType(), typeNames);
Expr *NumElts = vaType->getSizeExpr();
ArrayType::ArraySizeModifier ASM = vaType->getSizeModifier();
unsigned IndexTypeQuals = vaType->getIndexTypeCVRQualifiers();
SourceRange Brackets = vaType->getBracketsRange();
return getVariableArrayType(EltTy, NumElts, ASM, IndexTypeQuals, Brackets);
}
case Type::IncompleteArray: {
const IncompleteArrayType *iaType = cast<IncompleteArrayType>(T);
QualType EltTy = substituteTypeArgs(iaType->getElementType(), typeNames);
ArrayType::ArraySizeModifier ASM = iaType->getSizeModifier();
unsigned IndexTypeQuals = iaType->getIndexTypeCVRQualifiers();
CheckedArrayKind Kind = iaType->getKind();
return getIncompleteArrayType(EltTy, ASM, IndexTypeQuals, Kind);
}
case Type::DependentSizedArray: {
const DependentSizedArrayType *dsaType = cast<DependentSizedArrayType>(T);
QualType EltTy = substituteTypeArgs(dsaType->getElementType(), typeNames);
Expr *NumElts = dsaType->getSizeExpr();
ArrayType::ArraySizeModifier ASM = dsaType->getSizeModifier();
unsigned IndexTypeQuals = dsaType->getIndexTypeCVRQualifiers();
SourceRange Brackets = dsaType->getBracketsRange();
return getDependentSizedArrayType(EltTy, NumElts, ASM, IndexTypeQuals,
Brackets);
}
case Type::Paren: {
const ParenType *pType = dyn_cast<ParenType>(T);
if (!pType) {
llvm_unreachable("Dynamic cast failed unexpectedly.");
return QT;
}
QualType InnerType = substituteTypeArgs(pType->getInnerType(), typeNames);
return getParenType(InnerType);
}

case Type::FunctionProto: {
const FunctionProtoType *fpType = dyn_cast<FunctionProtoType>(T);
if (!fpType) {
llvm_unreachable("Dynamic cast failed unexpectedly.");
return QT;
}
// Initialize return type if it's type variable
BoundsAnnotations ReturnBAnnot = fpType->getReturnAnnots();
InteropTypeExpr *RBIT = ReturnBAnnot.getInteropTypeExpr();
QualType returnQualType;
if (RBIT) {
returnQualType = substituteTypeArgs(RBIT->getTypeAsWritten() , typeNames);
} else {
returnQualType = substituteTypeArgs(fpType->getReturnType() , typeNames);
}
// Initialize parameter types if it's type variable
SmallVector<QualType, 16> newParamTypes;

int iCnt = 0;
for (QualType opt : fpType->getParamTypes()) {
BoundsAnnotations BAnnot = fpType->getParamAnnots(iCnt);
InteropTypeExpr *BIT = BAnnot.getInteropTypeExpr();
if (BIT) {
newParamTypes.push_back(substituteTypeArgs(BIT->getTypeAsWritten(), typeNames));
} else {
newParamTypes.push_back(substituteTypeArgs(opt, typeNames));
}
iCnt++;
}
// Indicate that this function type is fully instantiated
FunctionProtoType::ExtProtoInfo newExtProtoInfo = fpType->getExtProtoInfo();
newExtProtoInfo.numTypeVars = 0;
newExtProtoInfo.GenericFunction = false;
newExtProtoInfo.ItypeGenericFunction = false;

// Recreate FunctionProtoType, and set it as the new type for declRefExpr
return getFunctionType(returnQualType, newParamTypes, newExtProtoInfo);
}
//bool changed = false;
//const FunctionProtoType *fpType = cast<FunctionProtoType>(T);

//// Make a copy of the ExtProtoInfo.
//FunctionProtoType::ExtProtoInfo EPI = fpType->getExtProtoInfo();

//// Handle return type and return bounds annotations.
//QualType returnQualType = substituteTypeArgs(fpType->getReturnType() , typeNames);
//if (returnQualType != fpType->getReturnType())
// changed = true;

//BoundsAnnotations ReturnAnnots = EPI.ReturnAnnots;
//InteropTypeExpr *RBIT = ReturnAnnots.getInteropTypeExpr();
//// TODO: substitute into return bounds expression.
//if (RBIT) {
// QualType ExistingType = RBIT->getTypeAsWritten();
// QualType NewType = substituteTypeArgs(ExistingType , typeNames);
// if (NewType != ExistingType) {
// TypeSourceInfo *TInfo =
// getTrivialTypeSourceInfo(NewType, SourceLocation());
// InteropTypeExpr *NewRBIT =
// new (*this) InteropTypeExpr(NewType, RBIT->getLocStart(), RBIT->getLocEnd(), TInfo);
// EPI.ReturnAnnots = BoundsAnnotations(ReturnAnnots.getBoundsExpr(), NewRBIT);
// changed = true;
// }
//}

//// Handle parameter types and parameter bounds annotations.
//// TODO: substitute into parameter bounds expressions.
//SmallVector<QualType, 8> newParamTypes;
//SmallVector<BoundsAnnotations, 8> newParamAnnots;

//int i = 0;
//for (QualType QT : fpType->getParamTypes()) {
// newParamTypes.push_back(substituteTypeArgs(QT, typeNames));
// BoundsAnnotations ParamAnnot = fpType->getParamAnnots(i);
// InteropTypeExpr *ParamInteropType = ParamAnnot.getInteropTypeExpr();
// InteropTypeExpr *NewParamInteropType = ParamInteropType;
// if (ParamInteropType) {
// QualType ExistingType = ParamInteropType->getTypeAsWritten();
// QualType NewType = substituteTypeArgs(ExistingType , typeNames);
// if (NewType != ExistingType) {
// TypeSourceInfo *TInfo =
// getTrivialTypeSourceInfo(NewType, SourceLocation());
// NewParamInteropType =
// new (*this) InteropTypeExpr(NewType, ParamInteropType->getLocStart(), ParamInteropType->getLocEnd(), TInfo);
// changed = true;
// }
// }
// newParamAnnots.push_back(BoundsAnnotations(ParamAnnot.getBoundsExpr(), NewParamInteropType));
// i++;
//}
//// Indicate that this function type is fully instantiated

//EPI.numTypeVars = 0;
//EPI.ParamAnnots = newParamAnnots.data();

//// Recreate FunctionProtoType, and set it as the new type for declRefExpr
//return getFunctionType(returnQualType, newParamTypes, EPI);
//}
case Type::Typedef: {
// If underlying type is TypeVariableType, must replace the entire
// TypedefType. If underlying type is not TypeVariableType, there cannot be
// a type variable type that this context can replace.
const TypedefType *tdType = cast<TypedefType>(T);
const Type *underlying = tdType->getDecl()->getUnderlyingType().getTypePtr();
if (const TypeVariableType *tvType = dyn_cast<TypeVariableType>(underlying))
return typeNames[tvType->GetIndex()].typeName;
else
return QT;
}
case Type::TypeVariable: {
// Although Type Variable Type is wrapped with Typedef Type, there may be
// transformations in clang that eliminates Typedef.
const TypeVariableType *tvType = cast<TypeVariableType>(T);
return typeNames[tvType->GetIndex()].typeName;
}
case Type::Decayed: {
const DecayedType *dType = cast<DecayedType>(T);
QualType decayedType = substituteTypeArgs(dType->getOriginalType(), typeNames);
return getDecayedType(decayedType);
}
case Type::Adjusted: {
const AdjustedType *aType = cast<AdjustedType>(T);
QualType origType = substituteTypeArgs(aType->getOriginalType(), typeNames);
QualType newType = substituteTypeArgs(aType->getAdjustedType(), typeNames);
return getAdjustedType(origType, newType);
}
default :
return QT;
}
}

//===----------------------------------------------------------------------===//
// Integer Predicates
//===----------------------------------------------------------------------===//
Expand Down
60 changes: 8 additions & 52 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1792,8 +1792,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
return LHS;
}

if (ParseGenericTypeArgumentList(LHS, Loc))
return ExprError();
LHS = ParseGenericTypeArgumentList(LHS, Loc);
if (LHS.isInvalid())
LHS = ExprError();

break;
}
Expand Down Expand Up @@ -3174,24 +3175,7 @@ ExprResult Parser::ParseBoundsExpression() {
// generic type. However, we don't have the AST support for this yet.
//
// Returns false if parsing succeeded and true if an error occurred.
bool Parser::ParseGenericTypeArgumentList(ExprResult &Res, SourceLocation Loc) {
// Make sure we have a generic function or function with a bounds-safe
// interface

if (Res.isInvalid())
return false;

// TODO: generalize this
DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(Res.get());
if (!declRef)
return false;

const FunctionProtoType *funcType =
declRef->getType()->getAs<FunctionProtoType>();

if (!funcType)
return false;

ExprResult Parser::ParseGenericTypeArgumentList(ExprResult Res, SourceLocation Loc) {
SmallVector<DeclRefExpr::GenericInstInfo::TypeArgument, 4> typeArgumentInfos;
bool firstTypeArgument = true;
// Expect to see a list of type names, followed by a '>'.
Expand All @@ -3202,7 +3186,7 @@ bool Parser::ParseGenericTypeArgumentList(ExprResult &Res, SourceLocation Loc) {
// We want to consume greater, but not consume semi
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
if (Tok.getKind() == tok::greater) ConsumeToken();
return true;
return ExprError();
}
} else
firstTypeArgument = false;
Expand All @@ -3214,7 +3198,7 @@ bool Parser::ParseGenericTypeArgumentList(ExprResult &Res, SourceLocation Loc) {
// We want to consume greater, but not consume semi
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
if (Tok.getKind() == tok::greater) ConsumeToken();
return true;
return ExprError();
}

TypeSourceInfo *TInfo;
Expand All @@ -3223,36 +3207,8 @@ bool Parser::ParseGenericTypeArgumentList(ExprResult &Res, SourceLocation Loc) {
}
ConsumeToken(); // consume '>' token

// Make sure that the number of type names equals the number of type variables in
// the function type.
if (funcType->getNumTypeVars() != typeArgumentInfos.size()) {
FunctionDecl* funDecl = dyn_cast<FunctionDecl>(declRef->getDecl());
if (!funcType->isGenericFunction() && !funcType->isItypeGenericFunction()) {
Diag(Loc,
diag::err_type_args_for_non_generic_expression);
// TODO: emit a note pointing to the declaration.
return true;
}

// The location of beginning of _For_any is stored in typeVariables
Diag(Loc,
diag::err_type_list_and_type_variable_num_mismatch);

if (funDecl)
Diag(funDecl->typeVariables()[0]->getLocStart(),
diag::note_type_variables_declared_at);

return true;
}
// Add parsed list of type names to declRefExpr for future references
declRef->SetGenericInstInfo(Actions.getASTContext(), typeArgumentInfos);

// Substitute type arguments for type variables in the function type of the
// DeclRefExpr.
QualType NewTy =
Actions.Context.substituteTypeArgs(declRef->getType(), typeArgumentInfos);
declRef->setType(NewTy);
return false;
auto TypeArgs = ArrayRef<DeclRefExpr::GenericInstInfo::TypeArgument>(typeArgumentInfos);
return Actions.ActOnTypeApplication(Res, Loc, TypeArgs);
}

bool Parser::ParseRelativeBoundsClauseForDecl(ExprResult &Expr) {
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_clang_library(clangSema
AttributeList.cpp
CheckedCAlias.cpp
CheckedCInterop.cpp
CheckedCSubst.cpp
CodeCompleteConsumer.cpp
DeclSpec.cpp
DelayedDiagnostic.cpp
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5507,7 +5507,7 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,

// Substitute Type Variables of Function Type in DeclRefExpr
QualType NewTy =
Context.substituteTypeArgs(DR->getType(), typeArgumentInfos);
SubstituteTypeArgs(DR->getType(), typeArgumentInfos);
DR->setType(NewTy);
} else {
// There is no DeclRef to modify. Emit an error for now.
Expand Down
Loading