From: Douglas Gregor Date: Sat, 6 Dec 2008 00:22:45 +0000 (+0000) Subject: Add support for calls to dependent names within templates, e.g., X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5c37de788529cd9180f22069970737a7208bd625;p=clang Add support for calls to dependent names within templates, e.g., template void f(T x) { g(x); // g is a dependent name, so don't even bother to look it up g(); // error: g is not a dependent name } Note that when we see "g(", we build a CXXDependentNameExpr. However, if none of the call arguments are type-dependent, we will force the resolution of the name "g" and replace the CXXDependentNameExpr with its result. GCC actually produces a nice error message when you make this mistake, and even offers to compile your code with -fpermissive. I'll do the former next, but I don't plan to do the latter. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60618 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp index 2ee113fcda..f3ddb23a5f 100644 --- a/Driver/PrintParserCallbacks.cpp +++ b/Driver/PrintParserCallbacks.cpp @@ -459,7 +459,8 @@ namespace { /// This provides the location of the left/right parens and a list of comma /// locations. There are guaranteed to be one fewer commas than arguments, /// unless there are zero arguments. - virtual ExprResult ActOnCallExpr(ExprTy *Fn, SourceLocation LParenLoc, + virtual ExprResult ActOnCallExpr(Scope *S, ExprTy *Fn, + SourceLocation LParenLoc, ExprTy **Args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc) { diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 1d0a516eaf..74df215293 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -632,6 +632,48 @@ public: static CXXDeleteExpr * CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// CXXDependentNameExpr - Represents a dependent name in C++ for +/// which we could not locate any definition. These names can only +/// occur as in the example below, with an unqualified call to a +/// function name whose arguments are dependent. +/// @code +/// template void f(T x) { +/// g(x); // g is a dependent name. +/// } +/// @endcode +class CXXDependentNameExpr : public Expr { + /// Name - The name that was present in the source code. + IdentifierInfo *Name; + + /// Loc - The location + SourceLocation Loc; + +public: + CXXDependentNameExpr(IdentifierInfo *N, QualType T, SourceLocation L) + : Expr(CXXDependentNameExprClass, T, true, true), Name(N), Loc(L) { } + + /// getName - Retrieves the name that occurred in the source code. + IdentifierInfo *getName() const { return Name; } + + /// getLocation - Retrieves the location in the source code where + /// the name occurred. + SourceLocation getLocation() const { return Loc; } + + virtual SourceRange getSourceRange() const { return SourceRange(Loc); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDependentNameExprClass; + } + static bool classof(const CXXDependentNameExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static CXXDependentNameExpr *CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + } // end namespace clang #endif diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index 4ba9f75fc4..b49ad8a232 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -107,6 +107,7 @@ STMT(CXXZeroInitValueExpr , Expr) STMT(CXXConditionDeclExpr , DeclRefExpr) STMT(CXXNewExpr , Expr) STMT(CXXDeleteExpr , Expr) +STMT(CXXDependentNameExpr , Expr) // Obj-C Expressions. STMT(ObjCStringLiteral , Expr) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 1695033db8..b25466d443 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -530,7 +530,8 @@ public: /// This provides the location of the left/right parens and a list of comma /// locations. There are guaranteed to be one fewer commas than arguments, /// unless there are zero arguments. - virtual ExprResult ActOnCallExpr(ExprTy *Fn, SourceLocation LParenLoc, + virtual ExprResult ActOnCallExpr(Scope *S, ExprTy *Fn, + SourceLocation LParenLoc, ExprTy **Args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc) { diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 2d517196be..b5d5b4c12f 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -112,6 +112,14 @@ Stmt::child_iterator CXXNewExpr::child_end() { Stmt::child_iterator CXXDeleteExpr::child_begin() { return &Argument; } Stmt::child_iterator CXXDeleteExpr::child_end() { return &Argument+1; } +// CXXDependentNameExpr +Stmt::child_iterator CXXDependentNameExpr::child_begin() { + return child_iterator(); +} +Stmt::child_iterator CXXDependentNameExpr::child_end() { + return child_iterator(); +} + OverloadedOperatorKind CXXOperatorCallExpr::getOperator() const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 2e54777b94..94bca63866 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -988,6 +988,10 @@ void StmtPrinter::VisitCXXDeleteExpr(CXXDeleteExpr *E) { PrintExpr(E->getArgument()); } +void StmtPrinter::VisitCXXDependentNameExpr(CXXDependentNameExpr *E) { + OS << E->getName()->getName(); +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 6ebaca1c8e..9b6207be2b 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -239,6 +239,9 @@ Stmt* Stmt::Create(Deserializer& D, ASTContext& C) { case CXXDeleteExprClass: return CXXDeleteExpr::CreateImpl(D, C); + + case CXXDependentNameExprClass: + return CXXDependentNameExpr::CreateImpl(D, C); } } @@ -1506,3 +1509,17 @@ CXXDeleteExpr::CreateImpl(Deserializer& D, ASTContext& C) { return new CXXDeleteExpr(Ty, GlobalDelete, ArrayForm, OperatorDelete, cast(Argument), Loc); } + +void CXXDependentNameExpr::EmitImpl(llvm::Serializer& S) const { + S.Emit(getType()); + S.EmitPtr(Name); + S.Emit(Loc); +} + +CXXDependentNameExpr * +CXXDependentNameExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { + QualType Ty = QualType::ReadVal(D); + IdentifierInfo *N = D.ReadPtr(); + SourceLocation L = SourceLocation::ReadVal(D); + return new CXXDependentNameExpr(N, Ty, L); +} diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 52caffbb7f..e31c7a586e 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -740,7 +740,8 @@ Parser::ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { if (!LHS.isInvalid && Tok.is(tok::r_paren)) { assert((ArgExprs.size() == 0 || ArgExprs.size()-1 == CommaLocs.size())&& "Unexpected number of commas!"); - LHS = Actions.ActOnCallExpr(LHSGuard.take(), Loc, ArgExprs.take(), + LHS = Actions.ActOnCallExpr(CurScope, LHSGuard.take(), Loc, + ArgExprs.take(), ArgExprs.size(), &CommaLocs[0], Tok.getLocation()); LHSGuard.reset(LHS); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 85cd2c873b..b396e1c185 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -461,7 +461,7 @@ public: SourceLocation RParenLoc); ExprResult - BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, + BuildCallToObjectOfClassType(Scope *S, Expr *Object, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc); @@ -643,7 +643,8 @@ public: ExprResult ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, DeclarationName Name, bool HasTrailingLParen, - const CXXScopeSpec *SS); + const CXXScopeSpec *SS, + bool ForceResolution = false); virtual ExprResult ActOnPredefinedExpr(SourceLocation Loc, @@ -681,7 +682,8 @@ public: /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments. /// This provides the location of the left/right parens and a list of comma /// locations. - virtual ExprResult ActOnCallExpr(ExprTy *Fn, SourceLocation LParenLoc, + virtual ExprResult ActOnCallExpr(Scope *S, ExprTy *Fn, + SourceLocation LParenLoc, ExprTy **Args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e4beb74fc5..cdca682704 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -364,10 +364,29 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, /// function call context. LookupCtx is only used for a C++ /// qualified-id (foo::bar) to indicate the class or namespace that /// the identifier must be a member of. +/// +/// If ForceResolution is true, then we will attempt to resolve the +/// name even if it looks like a dependent name. This option is off by +/// default. Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, DeclarationName Name, bool HasTrailingLParen, - const CXXScopeSpec *SS) { + const CXXScopeSpec *SS, + bool ForceResolution) { + if (S->getTemplateParamParent() && Name.getAsIdentifierInfo() && + HasTrailingLParen && !SS && !ForceResolution) { + // We've seen something of the form + // identifier( + // and we are in a template, so it is likely that 's' is a + // dependent name. However, we won't know until we've parsed all + // of the call arguments. So, build a CXXDependentNameExpr node + // to represent this name. Then, if it turns out that none of the + // arguments are type-dependent, we'll force the resolution of the + // dependent name at that point. + return new CXXDependentNameExpr(Name.getAsIdentifierInfo(), + Context.DependentTy, Loc); + } + // Could be enum-constant, value decl, instance variable, etc. Decl *D; if (SS && !SS->isEmpty()) { @@ -377,7 +396,7 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, D = LookupDecl(Name, Decl::IDNS_Ordinary, S, DC); } else D = LookupDecl(Name, Decl::IDNS_Ordinary, S); - + // If this reference is in an Objective-C method, then ivar lookup happens as // well. IdentifierInfo *II = Name.getAsIdentifierInfo(); @@ -1315,7 +1334,7 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc, /// This provides the location of the left/right parens and a list of comma /// locations. Action::ExprResult Sema:: -ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, +ActOnCallExpr(Scope *S, ExprTy *fn, SourceLocation LParenLoc, ExprTy **args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc) { Expr *Fn = static_cast(fn); @@ -1324,9 +1343,36 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, FunctionDecl *FDecl = NULL; OverloadedFunctionDecl *Ovl = NULL; + // Determine whether this is a dependent call inside a C++ template, + // in which case we won't do any semantic analysis now. + bool Dependent = false; + if (Fn->isTypeDependent()) { + if (CXXDependentNameExpr *FnName = dyn_cast(Fn)) { + if (Expr::hasAnyTypeDependentArguments(Args, NumArgs)) + Dependent = true; + else { + // Resolve the CXXDependentNameExpr to an actual identifier; + // it wasn't really a dependent name after all. + ExprResult Resolved + = ActOnDeclarationNameExpr(S, FnName->getLocation(), FnName->getName(), + /*HasTrailingLParen=*/true, + /*SS=*/0, + /*ForceResolution=*/true); + if (Resolved.isInvalid) + return true; + else { + delete Fn; + Fn = (Expr *)Resolved.Val; + } + } + } else + Dependent = true; + } else + Dependent = Expr::hasAnyTypeDependentArguments(Args, NumArgs); + // FIXME: Will need to cache the results of name lookup (including // ADL) in Fn. - if (Fn->isTypeDependent() || Expr::hasAnyTypeDependentArguments(Args, NumArgs)) + if (Dependent) return new CallExpr(Fn, Args, NumArgs, Context.DependentTy, RParenLoc); // If we're directly calling a function or a set of overloaded @@ -1358,7 +1404,7 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, } if (getLangOptions().CPlusPlus && Fn->getType()->isRecordType()) - return BuildCallToObjectOfClassType(Fn, LParenLoc, Args, NumArgs, + return BuildCallToObjectOfClassType(S, Fn, LParenLoc, Args, NumArgs, CommaLocs, RParenLoc); // Promote the function operand. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 80bb9eda88..8a68d5be5c 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -3102,7 +3102,8 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, OverloadedFunctionDecl *Ov /// overloaded function call operator (@c operator()) or performing a /// user-defined conversion on the object argument. Action::ExprResult -Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, +Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, + SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, SourceLocation RParenLoc) { @@ -3220,7 +3221,7 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, ImpCastExprToType(Object, Conv->getConversionType().getNonReferenceType(), Conv->getConversionType()->isReferenceType()); - return ActOnCallExpr((ExprTy*)Object, LParenLoc, (ExprTy**)Args, NumArgs, + return ActOnCallExpr(S, (ExprTy*)Object, LParenLoc, (ExprTy**)Args, NumArgs, CommaLocs, RParenLoc); } diff --git a/test/SemaCXX/type-dependent-exprs.cpp b/test/SemaCXX/type-dependent-exprs.cpp index d3fd8922ac..8d71073a0c 100644 --- a/test/SemaCXX/type-dependent-exprs.cpp +++ b/test/SemaCXX/type-dependent-exprs.cpp @@ -9,6 +9,8 @@ T f(T x) { (void)(x += 0); (void)(x? x : x); return g(x); - // h(x); // h is a dependent name + h(x); // h is a dependent name + g(1, 1); // expected-error{{too many arguments to function call}} + h(1); // expected-error{{use of undeclared identifier 'h'}} return 0; }