From: Douglas Gregor Date: Mon, 23 Mar 2009 23:06:20 +0000 (+0000) Subject: Template instantiation for the declarations of member functions within X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2dc0e64e57b2a1786fa53a7dbd1d5c8e255eadb0;p=clang Template instantiation for the declarations of member functions within a class template. At present, we can only instantiation normal methods, but not constructors, destructors, or conversion operators. As ever, this contains a bit of refactoring in Sema's type-checking. In particular: - Split ActOnFunctionDeclarator into ActOnFunctionDeclarator (handling the declarator itself) and CheckFunctionDeclaration (checking for the the function declaration), the latter of which is also used by template instantiation. - We were performing the adjustment of function parameter types in three places; collect those into a single new routine. - When the type of a parameter is adjusted, allocate an OriginalParmVarDecl to keep track of the type as it was written. - Eliminate a redundant check for out-of-line declarations of member functions; hide more C++-specific checks on function declarations behind if(getLangOptions().CPlusPlus). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67575 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index a071455276..ca5c299c02 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -290,6 +290,7 @@ public: //===--------------------------------------------------------------------===// // Type Analysis / Processing: SemaType.cpp. // + QualType adjustParameterType(QualType T); QualType ConvertDeclSpecToType(const DeclSpec &DS); void ProcessTypeAttributeList(QualType &Result, const AttributeList *AL); QualType BuildPointerType(QualType T, unsigned Quals, @@ -349,6 +350,9 @@ public: NamedDecl* PrevDecl, bool IsFunctionDefinition, bool& InvalidDecl, bool &Redeclaration); + bool CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl, + bool &Redeclaration, + bool &OverloadableAttrRequired); virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D); virtual void ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index d71417b957..ccc3197446 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1437,7 +1437,7 @@ Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, NamedDecl *PrevDecl, // If there was a previous declaration of this variable, it may be // in our identifier chain. Update the identifier chain with the new // declaration. - if (IdResolver.ReplaceDecl(PrevDecl, ND)) { + if (S && IdResolver.ReplaceDecl(PrevDecl, ND)) { // The previous declaration was found on the identifer resolver // chain, so remove it from its scope. while (S && !S->isDeclScope(PrevDecl)) @@ -2034,23 +2034,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, NewFD->setParams(Context, &Params[0], Params.size()); } } - - if (CXXConstructorDecl *Constructor = dyn_cast(NewFD)) - InvalidDecl = InvalidDecl || CheckConstructor(Constructor); - else if (isa(NewFD)) { - CXXRecordDecl *Record = cast(NewFD->getParent()); - Record->setUserDeclaredDestructor(true); - // C++ [class]p4: A POD-struct is an aggregate class that has [...] no - // user-defined destructor. - Record->setPOD(false); - } else if (CXXConversionDecl *Conversion = - dyn_cast(NewFD)) - ActOnConversionDeclarator(Conversion); - - // Extra checking for C++ overloaded operators (C++ [over.oper]). - if (NewFD->isOverloadedOperator() && - CheckOverloadedOperatorDeclaration(NewFD)) - NewFD->setInvalidDecl(); // If name lookup finds a previous declaration that is not in the // same scope as the new declaration, this may still be an @@ -2060,19 +2043,130 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, isOutOfScopePreviousDeclaration(PrevDecl, DC, Context))) PrevDecl = 0; + // Perform semantic checking on the function declaration. + bool OverloadableAttrRequired = false; // FIXME: HACK! + if (CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration, + /*FIXME:*/OverloadableAttrRequired)) + InvalidDecl = true; + + if (D.getCXXScopeSpec().isSet() && !InvalidDecl) { + // An out-of-line member function declaration must also be a + // definition (C++ [dcl.meaning]p1). + if (!IsFunctionDefinition) { + Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) + << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + } else if (!Redeclaration) { + // The user tried to provide an out-of-line definition for a + // function that is a member of a class or namespace, but there + // was no such member function declared (C++ [class.mfct]p2, + // C++ [namespace.memdef]p2). For example: + // + // class X { + // void f() const; + // }; + // + // void X::f() { } // ill-formed + // + // Complain about this problem, and attempt to suggest close + // matches (e.g., those that differ only in cv-qualifiers and + // whether the parameter types are references). + Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match) + << cast(DC) << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + + LookupResult Prev = LookupQualifiedName(DC, Name, LookupOrdinaryName, + true); + assert(!Prev.isAmbiguous() && + "Cannot have an ambiguity in previous-declaration lookup"); + for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end(); + Func != FuncEnd; ++Func) { + if (isa(*Func) && + isNearlyMatchingFunction(Context, cast(*Func), NewFD)) + Diag((*Func)->getLocation(), diag::note_member_def_close_match); + } + + PrevDecl = 0; + } + } + + // Handle attributes. We need to have merged decls when handling attributes + // (for example to check for conflicts, etc). + // FIXME: This needs to happen before we merge declarations. Then, + // let attribute merging cope with attribute conflicts. + ProcessDeclAttributes(NewFD, D); + AddKnownFunctionAttributes(NewFD); + + if (OverloadableAttrRequired && !NewFD->getAttr()) { + // If a function name is overloadable in C, then every function + // with that name must be marked "overloadable". + Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing) + << Redeclaration << NewFD; + if (PrevDecl) + Diag(PrevDecl->getLocation(), + diag::note_attribute_overloadable_prev_overload); + NewFD->addAttr(::new (Context) OverloadableAttr()); + } + + // If this is a locally-scoped extern C function, update the + // map of such names. + if (CurContext->isFunctionOrMethod() && NewFD->isExternC(Context) + && !InvalidDecl) + RegisterLocallyScopedExternCDecl(NewFD, PrevDecl, S); + + return NewFD; +} + +/// \brief Perform semantic checking of a new function declaration. +/// +/// Performs semantic analysis of the new function declaration +/// NewFD. This routine performs all semantic checking that does not +/// require the actual declarator involved in the declaration, and is +/// used both for the declaration of functions as they are parsed +/// (called via ActOnDeclarator) and for the declaration of functions +/// that have been instantiated via C++ template instantiation (called +/// via InstantiateDecl). +/// +/// \returns true if there was an error, false otherwise. +bool Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl, + bool &Redeclaration, + bool &OverloadableAttrRequired) { + bool InvalidDecl = false; + + // Semantic checking for this function declaration (in isolation). + if (getLangOptions().CPlusPlus) { + // C++-specific checks. + if (CXXConstructorDecl *Constructor = dyn_cast(NewFD)) + InvalidDecl = InvalidDecl || CheckConstructor(Constructor); + else if (isa(NewFD)) { + CXXRecordDecl *Record = cast(NewFD->getParent()); + Record->setUserDeclaredDestructor(true); + // C++ [class]p4: A POD-struct is an aggregate class that has [...] no + // user-defined destructor. + Record->setPOD(false); + } else if (CXXConversionDecl *Conversion + = dyn_cast(NewFD)) + ActOnConversionDeclarator(Conversion); + + // Extra checking for C++ overloaded operators (C++ [over.oper]). + if (NewFD->isOverloadedOperator() && + CheckOverloadedOperatorDeclaration(NewFD)) + InvalidDecl = true; + } + + // Check for a previous declaration of this name. if (!PrevDecl && NewFD->isExternC(Context)) { // Since we did not find anything by this name and we're declaring // an extern "C" function, look for a non-visible extern "C" // declaration with the same name. llvm::DenseMap::iterator Pos - = LocallyScopedExternalDecls.find(Name); + = LocallyScopedExternalDecls.find(NewFD->getDeclName()); if (Pos != LocallyScopedExternalDecls.end()) PrevDecl = Pos->second; } // Merge or overload the declaration with an existing declaration of // the same name, if appropriate. - bool OverloadableAttrRequired = false; if (PrevDecl) { // Determine whether NewFD is an overload of PrevDecl or // a declaration that requires merging. If it's an overload, @@ -2086,15 +2180,16 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Functions marked "overloadable" must have a prototype (that // we can't get through declaration merging). - if (!R->getAsFunctionProtoType()) { + if (!NewFD->getType()->getAsFunctionProtoType()) { Diag(NewFD->getLocation(), diag::err_attribute_overloadable_no_prototype) << NewFD; InvalidDecl = true; Redeclaration = true; // Turn this into a variadic function with no parameters. - R = Context.getFunctionType(R->getAsFunctionType()->getResultType(), - 0, 0, true, 0); + QualType R = Context.getFunctionType( + NewFD->getType()->getAsFunctionType()->getResultType(), + 0, 0, true, 0); NewFD->setType(R); } } @@ -2110,99 +2205,24 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (isa(PrevDecl)) OldDecl = *MatchedDecl; - // NewFD and PrevDecl represent declarations that need to be + // NewFD and OldDecl represent declarations that need to be // merged. if (MergeFunctionDecl(NewFD, OldDecl)) InvalidDecl = true; - if (!InvalidDecl) { + if (!InvalidDecl) NewFD->setPreviousDeclaration(cast(OldDecl)); - - // An out-of-line member function declaration must also be a - // definition (C++ [dcl.meaning]p1). - if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() && - !InvalidDecl) { - Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) - << D.getCXXScopeSpec().getRange(); - NewFD->setInvalidDecl(); - } - } - } - } - - if (D.getCXXScopeSpec().isSet() && - (!PrevDecl || !Redeclaration)) { - // The user tried to provide an out-of-line definition for a - // function that is a member of a class or namespace, but there - // was no such member function declared (C++ [class.mfct]p2, - // C++ [namespace.memdef]p2). For example: - // - // class X { - // void f() const; - // }; - // - // void X::f() { } // ill-formed - // - // Complain about this problem, and attempt to suggest close - // matches (e.g., those that differ only in cv-qualifiers and - // whether the parameter types are references). - Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match) - << cast(DC) << D.getCXXScopeSpec().getRange(); - InvalidDecl = true; - - LookupResult Prev = LookupQualifiedName(DC, Name, LookupOrdinaryName, - true); - assert(!Prev.isAmbiguous() && - "Cannot have an ambiguity in previous-declaration lookup"); - for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end(); - Func != FuncEnd; ++Func) { - if (isa(*Func) && - isNearlyMatchingFunction(Context, cast(*Func), NewFD)) - Diag((*Func)->getLocation(), diag::note_member_def_close_match); } - - PrevDecl = 0; } - // Handle attributes. We need to have merged decls when handling attributes - // (for example to check for conflicts, etc). - ProcessDeclAttributes(NewFD, D); - AddKnownFunctionAttributes(NewFD); - - if (OverloadableAttrRequired && !NewFD->getAttr()) { - // If a function name is overloadable in C, then every function - // with that name must be marked "overloadable". - Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing) - << Redeclaration << NewFD; - if (PrevDecl) - Diag(PrevDecl->getLocation(), - diag::note_attribute_overloadable_prev_overload); - NewFD->addAttr(::new (Context) OverloadableAttr()); - } - - if (getLangOptions().CPlusPlus) { + if (getLangOptions().CPlusPlus && !CurContext->isRecord()) { // In C++, check default arguments now that we have merged decls. Unless // the lexical context is the class, because in this case this is done // during delayed parsing anyway. - if (!CurContext->isRecord()) - CheckCXXDefaultArguments(NewFD); - - // An out-of-line member function declaration must also be a - // definition (C++ [dcl.meaning]p1). - if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() && !InvalidDecl) { - Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) - << D.getCXXScopeSpec().getRange(); - InvalidDecl = true; - } + CheckCXXDefaultArguments(NewFD); } - // If this is a locally-scoped extern C function, update the - // map of such names. - if (CurContext->isFunctionOrMethod() && NewFD->isExternC(Context) - && !InvalidDecl) - RegisterLocallyScopedExternCDecl(NewFD, PrevDecl, S); - - return NewFD; + return InvalidDecl || NewFD->isInvalidDecl(); } bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) { @@ -2588,40 +2608,24 @@ Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { } } - // Perform the default function/array conversion (C99 6.7.5.3p[7,8]). - // Doing the promotion here has a win and a loss. The win is the type for - // both Decl's and DeclRefExpr's will match (a convenient invariant for the - // code generator). The loss is the orginal type isn't preserved. For example: - // - // void func(int parmvardecl[5]) { // convert "int [5]" to "int *" - // int blockvardecl[5]; - // sizeof(parmvardecl); // size == 4 - // sizeof(blockvardecl); // size == 20 - // } - // - // For expressions, all implicit conversions are captured using the - // ImplicitCastExpr AST node (we have no such mechanism for Decl's). - // - // FIXME: If a source translation tool needs to see the original type, then - // we need to consider storing both types (in ParmVarDecl)... - // - // Parameters can not be abstract class types. if (RequireNonAbstractType(D.getIdentifierLoc(), parmDeclType, diag::err_abstract_type_in_decl, 1 /* parameter type */)) D.setInvalidType(true); + + QualType T = adjustParameterType(parmDeclType); - if (parmDeclType->isArrayType()) { - // int x[restrict 4] -> int *restrict - parmDeclType = Context.getArrayDecayedType(parmDeclType); - } else if (parmDeclType->isFunctionType()) - parmDeclType = Context.getPointerType(parmDeclType); - - ParmVarDecl *New = ParmVarDecl::Create(Context, CurContext, - D.getIdentifierLoc(), II, - parmDeclType, StorageClass, - 0); + ParmVarDecl *New; + if (T == parmDeclType) // parameter type did not need adjustment + New = ParmVarDecl::Create(Context, CurContext, + D.getIdentifierLoc(), II, + parmDeclType, StorageClass, + 0); + else // keep track of both the adjusted and unadjusted types + New = OriginalParmVarDecl::Create(Context, CurContext, + D.getIdentifierLoc(), II, T, + parmDeclType, StorageClass, 0); if (D.getInvalidType()) New->setInvalidDecl(); @@ -2634,7 +2638,7 @@ Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { } // Parameter declarators cannot be interface types. All ObjC objects are // passed by reference. - if (parmDeclType->isObjCInterfaceType()) { + if (T->isObjCInterfaceType()) { Diag(D.getIdentifierLoc(), diag::err_object_cannot_be_by_value) << "passed"; New->setInvalidDecl(); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 906ea25822..d8e526c602 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -43,6 +43,9 @@ namespace { Decl *VisitFieldDecl(FieldDecl *D); Decl *VisitStaticAssertDecl(StaticAssertDecl *D); Decl *VisitEnumDecl(EnumDecl *D); + Decl *VisitCXXMethodDecl(CXXMethodDecl *D); + Decl *VisitParmVarDecl(ParmVarDecl *D); + Decl *VisitOriginalParmVarDecl(OriginalParmVarDecl *D); // Base case. FIXME: Remove once we can instantiate everything. Decl *VisitDecl(Decl *) { @@ -192,6 +195,121 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { return Enum; } +Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) { + // Only handle actual methods; we'll deal with constructors, + // destructors, etc. separately. + if (D->getKind() != Decl::CXXMethod) + return 0; + + QualType T = SemaRef.InstantiateType(D->getType(), TemplateArgs, + NumTemplateArgs, D->getLocation(), + D->getDeclName()); + if (T.isNull()) + return 0; + + // Build the instantiated method declaration. + CXXRecordDecl *Record = cast(Owner); + CXXMethodDecl *Method + = CXXMethodDecl::Create(SemaRef.Context, Record, D->getLocation(), + D->getDeclName(), T, D->isStatic(), + D->isInline()); + Method->setAccess(D->getAccess()); + // FIXME: Duplicates some logic in ActOnFunctionDeclarator. + if (D->isVirtual()) { + Method->setVirtual(); + Record->setAggregate(false); + Record->setPOD(false); + Record->setPolymorphic(true); + } + if (D->isDeleted()) + Method->setDeleted(); + if (D->isPure()) { + Method->setPure(); + Record->setAbstract(true); + } + // FIXME: attributes + // FIXME: Method needs a pointer referencing where it came from. + + // Instantiate the function parameters + { + TemplateDeclInstantiator ParamInstantiator(SemaRef, Method, + TemplateArgs, NumTemplateArgs); + llvm::SmallVector Params; + for (FunctionDecl::param_iterator P = Method->param_begin(), + PEnd = Method->param_end(); + P != PEnd; ++P) { + if (ParmVarDecl *PInst = (ParmVarDecl *)ParamInstantiator.Visit(*P)) + Params.push_back(PInst); + else + Method->setInvalidDecl(); + } + } + + NamedDecl *PrevDecl + = SemaRef.LookupQualifiedName(Owner, Method->getDeclName(), + Sema::LookupOrdinaryName, true); + // In C++, the previous declaration we find might be a tag type + // (class or enum). In this case, the new declaration will hide the + // tag type. Note that this does does not apply if we're declaring a + // typedef (C++ [dcl.typedef]p4). + if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag) + PrevDecl = 0; + bool Redeclaration = false; + bool OverloadableAttrRequired = false; + if (SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration, + /*FIXME:*/OverloadableAttrRequired)) + Method->setInvalidDecl(); + + if (!Method->isInvalidDecl() || !PrevDecl) + Owner->addDecl(Method); + return Method; +} + +Decl *TemplateDeclInstantiator::VisitParmVarDecl(ParmVarDecl *D) { + QualType OrigT = SemaRef.InstantiateType(D->getOriginalType(), TemplateArgs, + NumTemplateArgs, D->getLocation(), + D->getDeclName()); + if (OrigT.isNull()) + return 0; + + QualType T = SemaRef.adjustParameterType(OrigT); + + if (D->getDefaultArg()) { + // FIXME: Leave a marker for "uninstantiated" default + // arguments. They only get instantiated on demand at the call + // site. + unsigned DiagID = SemaRef.Diags.getCustomDiagID(Diagnostic::Warning, + "sorry, dropping default argument during template instantiation"); + SemaRef.Diag(D->getDefaultArg()->getSourceRange().getBegin(), DiagID) + << D->getDefaultArg()->getSourceRange(); + } + + // Allocate the parameter + ParmVarDecl *Param = 0; + if (T == OrigT) + Param = ParmVarDecl::Create(SemaRef.Context, Owner, D->getLocation(), + D->getIdentifier(), T, D->getStorageClass(), + 0); + else + Param = OriginalParmVarDecl::Create(SemaRef.Context, Owner, + D->getLocation(), D->getIdentifier(), + T, OrigT, D->getStorageClass(), 0); + + // Note: we don't try to instantiate function parameters until after + // we've instantiated the function's type. Therefore, we don't have + // to check for 'void' parameter types here. + return Param; +} + +Decl * +TemplateDeclInstantiator::VisitOriginalParmVarDecl(OriginalParmVarDecl *D) { + // Since parameter types can decay either before or after + // instantiation, we simply treat OriginalParmVarDecls as + // ParmVarDecls the same way, and create one or the other depending + // on what happens after template instantiation. + return VisitParmVarDecl(D); +} + Decl *Sema::InstantiateDecl(Decl *D, DeclContext *Owner, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 3f4e78cf28..60ecb7abf7 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -19,6 +19,30 @@ #include "clang/Parse/DeclSpec.h" using namespace clang; +/// \brief Perform adjustment on the parameter type of a function. +/// +/// This routine adjusts the given parameter type @p T to the actual +/// parameter type used by semantic analysis (C99 6.7.5.3p[7,8], +/// C++ [dcl.fct]p3). The adjusted parameter type is returned. +QualType Sema::adjustParameterType(QualType T) { + // C99 6.7.5.3p7: + if (T->isArrayType()) { + // C99 6.7.5.3p7: + // A declaration of a parameter as "array of type" shall be + // adjusted to "qualified pointer to type", where the type + // qualifiers (if any) are those specified within the [ and ] of + // the array type derivation. + return Context.getArrayDecayedType(T); + } else if (T->isFunctionType()) + // C99 6.7.5.3p8: + // A declaration of a parameter as "function returning type" + // shall be adjusted to "pointer to function returning type", as + // in 6.3.2.1. + return Context.getPointerType(T); + + return T; +} + /// \brief Convert the specified declspec to the appropriate type /// object. /// \param DS the declaration specifiers @@ -523,12 +547,8 @@ QualType Sema::BuildFunctionType(QualType T, bool Invalid = false; for (unsigned Idx = 0; Idx < NumParamTypes; ++Idx) { - QualType ParamType = ParamTypes[Idx]; - if (ParamType->isArrayType()) - ParamType = Context.getArrayDecayedType(ParamType); - else if (ParamType->isFunctionType()) - ParamType = Context.getPointerType(ParamType); - else if (ParamType->isVoidType()) { + QualType ParamType = adjustParameterType(ParamTypes[Idx]); + if (ParamType->isVoidType()) { Diag(Loc, diag::err_param_with_void_type); Invalid = true; } @@ -683,29 +703,14 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { ParmVarDecl *Param = (ParmVarDecl *)FTI.ArgInfo[i].Param; QualType ArgTy = Param->getType(); assert(!ArgTy.isNull() && "Couldn't parse type?"); - // - // Perform the default function/array conversion (C99 6.7.5.3p[7,8]). - // This matches the conversion that is done in - // Sema::ActOnParamDeclarator(). Without this conversion, the - // argument type in the function prototype *will not* match the - // type in ParmVarDecl (which makes the code generator unhappy). - // - // FIXME: We still apparently need the conversion in - // Sema::ActOnParamDeclarator(). This doesn't make any sense, since - // it should be driving off the type being created here. - // - // FIXME: If a source translation tool needs to see the original type, - // then we need to consider storing both types somewhere... - // - if (ArgTy->isArrayType()) { - ArgTy = Context.getArrayDecayedType(ArgTy); - } else if (ArgTy->isFunctionType()) - ArgTy = Context.getPointerType(ArgTy); - + + // Adjust the parameter type. + ArgTy = adjustParameterType(ArgTy); + // Look for 'void'. void is allowed only as a single argument to a // function with no other parameters (C99 6.7.5.3p10). We record // int(void) as a FunctionProtoType with an empty argument list. - else if (ArgTy->isVoidType()) { + if (ArgTy->isVoidType()) { // If this is something like 'float(int, void)', reject it. 'void' // is an incomplete type (C99 6.2.5p19) and function decls cannot // have arguments of incomplete type. diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index eaca56c725..4b4d435f20 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -101,8 +101,7 @@ void f6(int A2::RC::x); // expected-error{{parameter declarator cannot be qualif int A2::RC::x; // expected-error{{non-static data member defined out-of-line}} -void A2::CC::NC::m(); // expected-error{{out-of-line declaration of a member must be a definition}} \ - // expected-error{{out-of-line declaration of a member must be a definition}} +void A2::CC::NC::m(); // expected-error{{out-of-line declaration of a member must be a definition}} namespace E { diff --git a/test/SemaTemplate/instantiate-method.cpp b/test/SemaTemplate/instantiate-method.cpp new file mode 100644 index 0000000000..2eb2b7a88c --- /dev/null +++ b/test/SemaTemplate/instantiate-method.cpp @@ -0,0 +1,43 @@ +// RUN: clang -fsyntax-only -verify %s + +template +class X { +public: + void f(T); // expected-error{{argument may not have 'void' type}} + // FIXME: source location isn't very good, because we're + // instantiating the type. Could we do better? + void g(T*); + + static int h(T, T); // expected-error 2{{argument may not have 'void' type}} +}; + +int identity(int x) { return x; } + +void test(X *xi, int *ip, X *xf) { + xi->f(17); + xi->g(ip); + xf->f(&identity); + xf->g(identity); + X::h(17, 25); + X::h(identity, &identity); +} + +void test_bad() { + X xv; // expected-note{{in instantiation of template class 'class X' requested here}} +} + +template +class Overloading { +public: + int& f(T, T); // expected-note{{previous declaration is here}} + float& f(T, U); // expected-error{{functions that differ only in their return type cannot be overloaded}} +}; + +void test_ovl(Overloading *oil, int i, long l) { + int &ir = oil->f(i, i); + float &fr = oil->f(i, l); +} + +void test_ovl_bad() { + Overloading off; // expected-note{{in instantiation of template class 'class Overloading' requested here}} +}