Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4771,8 +4771,12 @@ DefaultIgnore, InGroup; def err_template_param_default_arg_redefinition : Error< "template parameter redefines default argument">; +def err_template_param_default_arg_inconsistent_redefinition : Error< + "template parameter default argument is inconsistent with previous definition">; def note_template_param_prev_default_arg : Note< "previous default template argument defined here">; +def note_template_param_prev_default_arg_in_other_module : Note< + "previous default template argument defined in module %0">; def err_template_param_default_arg_missing : Error< "template parameter missing a default argument">; def ext_template_parameter_default_in_function_template : ExtWarn< Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -2624,6 +2624,76 @@ return false; } +static bool hasSameDefaultTemplateArgument(NamedDecl *X, NamedDecl *Y) { + ASTContext &C = X->getASTContext(); + // If the type parameter isn't the same already, we don't need to check the + // default argument further. + if (!C.isSameTemplateParameter(X, Y)) + return false; + + if (auto *TTPX = dyn_cast(X)) { + auto *TTPY = cast(Y); + if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) + return false; + + return C.hasSameType(TTPX->getDefaultArgument(), + TTPY->getDefaultArgument()); + } + + if (auto *NTTPX = dyn_cast(X)) { + auto *NTTPY = cast(Y); + if (!NTTPX->hasDefaultArgument() || !NTTPY->hasDefaultArgument()) + return false; + + Expr *DefaultArgumentX = NTTPX->getDefaultArgument()->IgnoreImpCasts(); + Expr *DefaultArgumentY = NTTPY->getDefaultArgument()->IgnoreImpCasts(); + if (DefaultArgumentX->getType()->isDependentType() || + DefaultArgumentY->getType()->isDependentType()) + return false; + + if (!C.hasSameType(DefaultArgumentX->getType(), + DefaultArgumentY->getType())) + return false; + + Expr::EvalResult EVRX, EVRY; + if (!DefaultArgumentX->EvaluateAsConstantExpr(EVRX, C) || + !DefaultArgumentY->EvaluateAsConstantExpr(EVRY, C)) + return false; + + APValue VX = EVRX.Val, VY = EVRY.Val; + if (VX.getKind() != VY.getKind()) + return false; + + if (VX.isInt()) + return llvm::APSInt::compareValues(VX.getInt(), VY.getInt()) == 0; + + if (VX.isLValue()) + return VX.getLValueBase() == VY.getLValueBase() && + VX.getLValueCallIndex() == VY.getLValueCallIndex() && + VX.getLValuePath() == VY.getLValuePath() && + VX.getLValueVersion() == VY.getLValueVersion(); + + assert( + VX.getKind() != APValue::FixedPoint && VX.getKind() != APValue::Float && + VX.getKind() != APValue::ComplexInt && + VX.getKind() != APValue::ComplexFloat && + VX.getKind() != APValue::Vector && VX.getKind() != APValue::Array && + "non-type template argument of such types should not be supported yet"); + + return false; + } + + auto *TTPX = cast(X); + auto *TTPY = cast(Y); + + if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) + return false; + + const TemplateArgument &TAX = TTPX->getDefaultArgument().getArgument(); + const TemplateArgument &TAY = TTPY->getDefaultArgument().getArgument(); + return C.hasSameTemplateName(TAX.getAsTemplate(), TAY.getAsTemplate()); +} + /// Checks the validity of a template parameter list, possibly /// considering the template parameter list from a previous /// declaration. @@ -2676,8 +2746,16 @@ for (TemplateParameterList::iterator NewParam = NewParams->begin(), NewParamEnd = NewParams->end(); NewParam != NewParamEnd; ++NewParam) { - // Variables used to diagnose redundant default arguments + // Variables used to diagnose redundant default arguments in the same + // translation unit. bool RedundantDefaultArg = false; + // Variables used to diagnose inconsitent default arguments in different + // translation unit. + bool InconsistentDefaultArg = false; + // Variables used to diagnose the module name of the old param in case the + // default argument of OldParam is inconsistent with NewParam. + std::string PrevModuleName; + SourceLocation OldDefaultLoc; SourceLocation NewDefaultLoc; @@ -2710,7 +2788,13 @@ OldDefaultLoc = OldTypeParm->getDefaultArgumentLoc(); NewDefaultLoc = NewTypeParm->getDefaultArgumentLoc(); SawDefaultArgument = true; - RedundantDefaultArg = true; + if (!OldTypeParm->getImportedOwningModule()) + RedundantDefaultArg = true; + else if (!hasSameDefaultTemplateArgument(OldTypeParm, NewTypeParm)) { + InconsistentDefaultArg = true; + PrevModuleName = + OldTypeParm->getImportedOwningModule()->getFullModuleName(); + } PreviousDefaultArgLoc = NewDefaultLoc; } else if (OldTypeParm && OldTypeParm->hasDefaultArgument()) { // Merge the default argument from the old declaration to the @@ -2755,7 +2839,14 @@ OldDefaultLoc = OldNonTypeParm->getDefaultArgumentLoc(); NewDefaultLoc = NewNonTypeParm->getDefaultArgumentLoc(); SawDefaultArgument = true; - RedundantDefaultArg = true; + if (!OldNonTypeParm->getImportedOwningModule()) + RedundantDefaultArg = true; + else if (!hasSameDefaultTemplateArgument(OldNonTypeParm, + NewNonTypeParm)) { + InconsistentDefaultArg = true; + PrevModuleName = + OldNonTypeParm->getImportedOwningModule()->getFullModuleName(); + } PreviousDefaultArgLoc = NewDefaultLoc; } else if (OldNonTypeParm && OldNonTypeParm->hasDefaultArgument()) { // Merge the default argument from the old declaration to the @@ -2799,7 +2890,14 @@ OldDefaultLoc = OldTemplateParm->getDefaultArgument().getLocation(); NewDefaultLoc = NewTemplateParm->getDefaultArgument().getLocation(); SawDefaultArgument = true; - RedundantDefaultArg = true; + if (!OldTemplateParm->getImportedOwningModule()) + RedundantDefaultArg = true; + else if (!hasSameDefaultTemplateArgument(OldTemplateParm, + NewTemplateParm)) { + InconsistentDefaultArg = true; + PrevModuleName = + OldTemplateParm->getImportedOwningModule()->getFullModuleName(); + } PreviousDefaultArgLoc = NewDefaultLoc; } else if (OldTemplateParm && OldTemplateParm->hasDefaultArgument()) { // Merge the default argument from the old declaration to the @@ -2826,13 +2924,32 @@ Invalid = true; } + // [basic.def.odr]/13: + // There can be more than one definition of a + // ... + // default template argument + // ... + // in a program provided that each definition appears in a different + // translation unit and the definitions satisfy the [same-meaning + // criteria of the ODR]. + // + // Simply, the design of modules allows the definition of template default + // argument to be repeated across translation unit. Note that the ODR is + // checked elsewhere. But it is still not allowed to repeat template default + // argument in the same translation unit. if (RedundantDefaultArg) { - // C++ [temp.param]p12: - // A template-parameter shall not be given default arguments - // by two different declarations in the same scope. Diag(NewDefaultLoc, diag::err_template_param_default_arg_redefinition); Diag(OldDefaultLoc, diag::note_template_param_prev_default_arg); Invalid = true; + } else if (InconsistentDefaultArg) { + // We could only diagnose about the case that the OldParam is imported. + // The case NewParam is imported should be handled in ASTReader. + Diag(NewDefaultLoc, + diag::err_template_param_default_arg_inconsistent_redefinition); + Diag(OldDefaultLoc, + diag::note_template_param_prev_default_arg_in_other_module) + << PrevModuleName; + Invalid = true; } else if (MissingDefaultArg && TPC != TPC_FunctionTemplate) { // C++ [temp.param]p11: // If a template-parameter of a class template has a default Index: clang/test/Modules/Inputs/redundant-template-default-arg/foo.h =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/redundant-template-default-arg/foo.h @@ -0,0 +1,37 @@ +template +T u; + +template +T v; + +template +int v2; + +template +class my_array {}; + +template