[clang] extend getCommonSugaredType to merge sugar nodes
authorMatheus Izvekov <mizvekov@gmail.com>
Tue, 19 Jul 2022 09:02:32 +0000 (11:02 +0200)
committerMatheus Izvekov <mizvekov@gmail.com>
Fri, 16 Sep 2022 15:04:10 +0000 (17:04 +0200)
This continues D111283 by extending the getCommonSugaredType
implementation to also merge non-canonical type nodes.

We merge these nodes by going up starting from the canonical
node, calculating their merged properties on the way.

If we reach a pair that is too different, or which we could not
otherwise unify, we bail out and don't try to keep going on to
the next pair, in effect striping out all the remaining top-level
sugar nodes. This avoids mismatching 'companion' nodes, such as
ElaboratedType, so that they don't end up elaborating some other
unrelated thing.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>
Differential Revision: https://reviews.llvm.org/D130308

clang/docs/ReleaseNotes.rst
clang/include/clang/AST/ASTContext.h
clang/lib/AST/ASTContext.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/test/SemaCXX/sugar-common-types.cpp
clang/test/SemaTemplate/deduction.cpp

index f897c68..e4648cd 100644 (file)
@@ -156,6 +156,12 @@ Improvements to Clang's diagnostics
 - Clang will now give a more suitale diagnostic for declaration of block
   scope identifiers that have external/internal linkage that has an initializer.
   Fixes `Issue 57478: <https://github.com/llvm/llvm-project/issues/57478>`_.
+- New analysis pass will now help preserve sugar when combining deductions, in an
+  order agnostic way. This will be in effect when deducing template arguments,
+  when deducing function return type from multiple return statements, for the
+  conditional operator, and for most binary operations. Type sugar is combined
+  in a way that strips the sugar which is different between terms, and preserves
+  those which are common.
 
 Non-comprehensive list of changes in this release
 -------------------------------------------------
index 39f5b4d..c022846 100644 (file)
@@ -1366,6 +1366,9 @@ public:
   CanQualType getDecayedType(CanQualType T) const {
     return CanQualType::CreateUnsafe(getDecayedType((QualType) T));
   }
+  /// Return the uniqued reference to a specified decay from the original
+  /// type to the decayed type.
+  QualType getDecayedType(QualType Orig, QualType Decayed) const;
 
   /// Return the uniqued reference to the atomic type for the specified
   /// type.
index c3e6f74..c326435 100644 (file)
@@ -3332,6 +3332,26 @@ QualType ASTContext::getAdjustedType(QualType Orig, QualType New) const {
   return QualType(AT, 0);
 }
 
+QualType ASTContext::getDecayedType(QualType Orig, QualType Decayed) const {
+  llvm::FoldingSetNodeID ID;
+  AdjustedType::Profile(ID, Orig, Decayed);
+  void *InsertPos = nullptr;
+  AdjustedType *AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos);
+  if (AT)
+    return QualType(AT, 0);
+
+  QualType Canonical = getCanonicalType(Decayed);
+
+  // Get the new insert position for the node we care about.
+  AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos);
+  assert(!AT && "Shouldn't be in the map!");
+
+  AT = new (*this, TypeAlignment) DecayedType(Orig, Decayed, Canonical);
+  Types.push_back(AT);
+  AdjustedTypes.InsertNode(AT, InsertPos);
+  return QualType(AT, 0);
+}
+
 QualType ASTContext::getDecayedType(QualType T) const {
   assert((T->isArrayType() || T->isFunctionType()) && "T does not decay");
 
@@ -3352,23 +3372,7 @@ QualType ASTContext::getDecayedType(QualType T) const {
   if (T->isFunctionType())
     Decayed = getPointerType(T);
 
-  llvm::FoldingSetNodeID ID;
-  AdjustedType::Profile(ID, T, Decayed);
-  void *InsertPos = nullptr;
-  AdjustedType *AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos);
-  if (AT)
-    return QualType(AT, 0);
-
-  QualType Canonical = getCanonicalType(Decayed);
-
-  // Get the new insert position for the node we care about.
-  AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos);
-  assert(!AT && "Shouldn't be in the map!");
-
-  AT = new (*this, TypeAlignment) DecayedType(T, Decayed, Canonical);
-  Types.push_back(AT);
-  AdjustedTypes.InsertNode(AT, InsertPos);
-  return QualType(AT, 0);
+  return getDecayedType(T, Decayed);
 }
 
 /// getBlockPointerType - Return the uniqued reference to the type for
@@ -12195,11 +12199,19 @@ static TemplateName getCommonTemplateName(ASTContext &Ctx, TemplateName X,
   //        with more sugar. For example one could be a SubstTemplateTemplate*
   //        replacing the other.
   TemplateName CX = Ctx.getCanonicalTemplateName(X);
-  assert(CX.getAsVoidPointer() ==
-         Ctx.getCanonicalTemplateName(Y).getAsVoidPointer());
+  if (CX.getAsVoidPointer() !=
+      Ctx.getCanonicalTemplateName(Y).getAsVoidPointer())
+    return TemplateName();
   return CX;
 }
 
+static TemplateName
+getCommonTemplateNameChecked(ASTContext &Ctx, TemplateName X, TemplateName Y) {
+  TemplateName R = getCommonTemplateName(Ctx, X, Y);
+  assert(R.getAsVoidPointer() != nullptr);
+  return R;
+}
+
 static auto getCommonTypes(ASTContext &Ctx, ArrayRef<QualType> Xs,
                            ArrayRef<QualType> Ys, bool Unqualified = false) {
   assert(Xs.size() == Ys.size());
@@ -12218,27 +12230,71 @@ static SourceLocation getCommonAttrLoc(const T *X, const T *Y) {
 static TemplateArgument getCommonTemplateArgument(ASTContext &Ctx,
                                                   const TemplateArgument &X,
                                                   const TemplateArgument &Y) {
-  assert(X.getKind() == Y.getKind());
+  if (X.getKind() != Y.getKind())
+    return TemplateArgument();
+
   switch (X.getKind()) {
   case TemplateArgument::ArgKind::Type:
+    if (!Ctx.hasSameType(X.getAsType(), Y.getAsType()))
+      return TemplateArgument();
     return TemplateArgument(
         Ctx.getCommonSugaredType(X.getAsType(), Y.getAsType()));
   case TemplateArgument::ArgKind::NullPtr:
+    if (!Ctx.hasSameType(X.getNullPtrType(), Y.getNullPtrType()))
+      return TemplateArgument();
     return TemplateArgument(
         Ctx.getCommonSugaredType(X.getNullPtrType(), Y.getNullPtrType()),
         /*Unqualified=*/true);
+  case TemplateArgument::ArgKind::Expression:
+    if (!Ctx.hasSameType(X.getAsExpr()->getType(), Y.getAsExpr()->getType()))
+      return TemplateArgument();
+    // FIXME: Try to keep the common sugar.
+    return X;
+  case TemplateArgument::ArgKind::Template: {
+    TemplateName TX = X.getAsTemplate(), TY = Y.getAsTemplate();
+    TemplateName CTN = ::getCommonTemplateName(Ctx, TX, TY);
+    if (!CTN.getAsVoidPointer())
+      return TemplateArgument();
+    return TemplateArgument(CTN);
+  }
+  case TemplateArgument::ArgKind::TemplateExpansion: {
+    TemplateName TX = X.getAsTemplateOrTemplatePattern(),
+                 TY = Y.getAsTemplateOrTemplatePattern();
+    TemplateName CTN = ::getCommonTemplateName(Ctx, TX, TY);
+    if (!CTN.getAsVoidPointer())
+      return TemplateName();
+    auto NExpX = X.getNumTemplateExpansions();
+    assert(NExpX == Y.getNumTemplateExpansions());
+    return TemplateArgument(CTN, NExpX);
+  }
   default:
     // FIXME: Handle the other argument kinds.
     return X;
   }
 }
 
+static bool getCommonTemplateArguments(ASTContext &Ctx,
+                                       SmallVectorImpl<TemplateArgument> &R,
+                                       ArrayRef<TemplateArgument> Xs,
+                                       ArrayRef<TemplateArgument> Ys) {
+  if (Xs.size() != Ys.size())
+    return true;
+  R.resize(Xs.size());
+  for (size_t I = 0; I < R.size(); ++I) {
+    R[I] = getCommonTemplateArgument(Ctx, Xs[I], Ys[I]);
+    if (R[I].isNull())
+      return true;
+  }
+  return false;
+}
+
 static auto getCommonTemplateArguments(ASTContext &Ctx,
-                                       ArrayRef<TemplateArgument> X,
-                                       ArrayRef<TemplateArgument> Y) {
-  SmallVector<TemplateArgument, 8> R(X.size());
-  for (size_t I = 0; I < R.size(); ++I)
-    R[I] = getCommonTemplateArgument(Ctx, X[I], Y[I]);
+                                       ArrayRef<TemplateArgument> Xs,
+                                       ArrayRef<TemplateArgument> Ys) {
+  SmallVector<TemplateArgument, 8> R;
+  bool Different = getCommonTemplateArguments(Ctx, R, Xs, Ys);
+  assert(!Different);
+  (void)Different;
   return R;
 }
 
@@ -12384,10 +12440,11 @@ ASTContext::mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1,
   llvm_unreachable("invalid ExceptionSpecificationType");
 }
 
-static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
-                              SplitQualType &Y) {
-  Type::TypeClass TC = X.Ty->getTypeClass();
-  assert(TC == Y.Ty->getTypeClass());
+static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
+                                          Qualifiers &QX, const Type *Y,
+                                          Qualifiers &QY) {
+  Type::TypeClass TC = X->getTypeClass();
+  assert(TC == Y->getTypeClass());
   switch (TC) {
 #define UNEXPECTED_TYPE(Class, Kind)                                           \
   case Type::Class:                                                            \
@@ -12419,7 +12476,7 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
 #undef UNEXPECTED_TYPE
 
   case Type::Auto: {
-    const auto *AX = cast<AutoType>(X.Ty), *AY = cast<AutoType>(Y.Ty);
+    const auto *AX = cast<AutoType>(X), *AY = cast<AutoType>(Y);
     assert(AX->getDeducedType().isNull());
     assert(AY->getDeducedType().isNull());
     assert(AX->getKeyword() == AY->getKeyword());
@@ -12430,22 +12487,22 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
     return Ctx.getAutoType(QualType(), AX->getKeyword(),
                            AX->isInstantiationDependentType(),
                            AX->containsUnexpandedParameterPack(),
-                           getCommonDecl(AX->getTypeConstraintConcept(),
-                                         AY->getTypeConstraintConcept()),
+                           getCommonDeclChecked(AX->getTypeConstraintConcept(),
+                                                AY->getTypeConstraintConcept()),
                            As);
   }
   case Type::IncompleteArray: {
-    const auto *AX = cast<IncompleteArrayType>(X.Ty),
-               *AY = cast<IncompleteArrayType>(Y.Ty);
+    const auto *AX = cast<IncompleteArrayType>(X),
+               *AY = cast<IncompleteArrayType>(Y);
     return Ctx.getIncompleteArrayType(
-        getCommonArrayElementType(Ctx, AX, X.Quals, AY, Y.Quals),
+        getCommonArrayElementType(Ctx, AX, QX, AY, QY),
         getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY));
   }
   case Type::DependentSizedArray: {
-    const auto *AX = cast<DependentSizedArrayType>(X.Ty),
-               *AY = cast<DependentSizedArrayType>(Y.Ty);
+    const auto *AX = cast<DependentSizedArrayType>(X),
+               *AY = cast<DependentSizedArrayType>(Y);
     return Ctx.getDependentSizedArrayType(
-        getCommonArrayElementType(Ctx, AX, X.Quals, AY, Y.Quals),
+        getCommonArrayElementType(Ctx, AX, QX, AY, QY),
         getCommonSizeExpr(Ctx, AX, AY), getCommonSizeModifier(AX, AY),
         getCommonIndexTypeCVRQualifiers(AX, AY),
         AX->getBracketsRange() == AY->getBracketsRange()
@@ -12453,44 +12510,41 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
             : SourceRange());
   }
   case Type::ConstantArray: {
-    const auto *AX = cast<ConstantArrayType>(X.Ty),
-               *AY = cast<ConstantArrayType>(Y.Ty);
+    const auto *AX = cast<ConstantArrayType>(X),
+               *AY = cast<ConstantArrayType>(Y);
     assert(AX->getSize() == AY->getSize());
     const Expr *SizeExpr = Ctx.hasSameExpr(AX->getSizeExpr(), AY->getSizeExpr())
                                ? AX->getSizeExpr()
                                : nullptr;
     return Ctx.getConstantArrayType(
-        getCommonArrayElementType(Ctx, AX, X.Quals, AY, Y.Quals), AX->getSize(),
-        SizeExpr, getCommonSizeModifier(AX, AY),
-        getCommonIndexTypeCVRQualifiers(AX, AY));
+        getCommonArrayElementType(Ctx, AX, QX, AY, QY), AX->getSize(), SizeExpr,
+        getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY));
   }
   case Type::Atomic: {
-    const auto *AX = cast<AtomicType>(X.Ty), *AY = cast<AtomicType>(Y.Ty);
+    const auto *AX = cast<AtomicType>(X), *AY = cast<AtomicType>(Y);
     return Ctx.getAtomicType(
         Ctx.getCommonSugaredType(AX->getValueType(), AY->getValueType()));
   }
   case Type::Complex: {
-    const auto *CX = cast<ComplexType>(X.Ty), *CY = cast<ComplexType>(Y.Ty);
-    return Ctx.getComplexType(
-        getCommonArrayElementType(Ctx, CX, X.Quals, CY, Y.Quals));
+    const auto *CX = cast<ComplexType>(X), *CY = cast<ComplexType>(Y);
+    return Ctx.getComplexType(getCommonArrayElementType(Ctx, CX, QX, CY, QY));
   }
   case Type::Pointer: {
-    const auto *PX = cast<PointerType>(X.Ty), *PY = cast<PointerType>(Y.Ty);
+    const auto *PX = cast<PointerType>(X), *PY = cast<PointerType>(Y);
     return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY));
   }
   case Type::BlockPointer: {
-    const auto *PX = cast<BlockPointerType>(X.Ty),
-               *PY = cast<BlockPointerType>(Y.Ty);
+    const auto *PX = cast<BlockPointerType>(X), *PY = cast<BlockPointerType>(Y);
     return Ctx.getBlockPointerType(getCommonPointeeType(Ctx, PX, PY));
   }
   case Type::ObjCObjectPointer: {
-    const auto *PX = cast<ObjCObjectPointerType>(X.Ty),
-               *PY = cast<ObjCObjectPointerType>(Y.Ty);
+    const auto *PX = cast<ObjCObjectPointerType>(X),
+               *PY = cast<ObjCObjectPointerType>(Y);
     return Ctx.getObjCObjectPointerType(getCommonPointeeType(Ctx, PX, PY));
   }
   case Type::MemberPointer: {
-    const auto *PX = cast<MemberPointerType>(X.Ty),
-               *PY = cast<MemberPointerType>(Y.Ty);
+    const auto *PX = cast<MemberPointerType>(X),
+               *PY = cast<MemberPointerType>(Y);
     return Ctx.getMemberPointerType(
         getCommonPointeeType(Ctx, PX, PY),
         Ctx.getCommonSugaredType(QualType(PX->getClass(), 0),
@@ -12498,38 +12552,38 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
             .getTypePtr());
   }
   case Type::LValueReference: {
-    const auto *PX = cast<LValueReferenceType>(X.Ty),
-               *PY = cast<LValueReferenceType>(Y.Ty);
+    const auto *PX = cast<LValueReferenceType>(X),
+               *PY = cast<LValueReferenceType>(Y);
     // FIXME: Preserve PointeeTypeAsWritten.
     return Ctx.getLValueReferenceType(getCommonPointeeType(Ctx, PX, PY),
                                       PX->isSpelledAsLValue() ||
                                           PY->isSpelledAsLValue());
   }
   case Type::RValueReference: {
-    const auto *PX = cast<RValueReferenceType>(X.Ty),
-               *PY = cast<RValueReferenceType>(Y.Ty);
+    const auto *PX = cast<RValueReferenceType>(X),
+               *PY = cast<RValueReferenceType>(Y);
     // FIXME: Preserve PointeeTypeAsWritten.
     return Ctx.getRValueReferenceType(getCommonPointeeType(Ctx, PX, PY));
   }
   case Type::DependentAddressSpace: {
-    const auto *PX = cast<DependentAddressSpaceType>(X.Ty),
-               *PY = cast<DependentAddressSpaceType>(Y.Ty);
+    const auto *PX = cast<DependentAddressSpaceType>(X),
+               *PY = cast<DependentAddressSpaceType>(Y);
     assert(Ctx.hasSameExpr(PX->getAddrSpaceExpr(), PY->getAddrSpaceExpr()));
     return Ctx.getDependentAddressSpaceType(getCommonPointeeType(Ctx, PX, PY),
                                             PX->getAddrSpaceExpr(),
                                             getCommonAttrLoc(PX, PY));
   }
   case Type::FunctionNoProto: {
-    const auto *FX = cast<FunctionNoProtoType>(X.Ty),
-               *FY = cast<FunctionNoProtoType>(Y.Ty);
+    const auto *FX = cast<FunctionNoProtoType>(X),
+               *FY = cast<FunctionNoProtoType>(Y);
     assert(FX->getExtInfo() == FY->getExtInfo());
     return Ctx.getFunctionNoProtoType(
         Ctx.getCommonSugaredType(FX->getReturnType(), FY->getReturnType()),
         FX->getExtInfo());
   }
   case Type::FunctionProto: {
-    const auto *FX = cast<FunctionProtoType>(X.Ty),
-               *FY = cast<FunctionProtoType>(Y.Ty);
+    const auto *FX = cast<FunctionProtoType>(X),
+               *FY = cast<FunctionProtoType>(Y);
     FunctionProtoType::ExtProtoInfo EPIX = FX->getExtProtoInfo(),
                                     EPIY = FY->getExtProtoInfo();
     assert(EPIX.ExtInfo == EPIY.ExtInfo);
@@ -12554,8 +12608,7 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
     return Ctx.getFunctionType(R, P, EPIX);
   }
   case Type::ObjCObject: {
-    const auto *OX = cast<ObjCObjectType>(X.Ty),
-               *OY = cast<ObjCObjectType>(Y.Ty);
+    const auto *OX = cast<ObjCObjectType>(X), *OY = cast<ObjCObjectType>(Y);
     assert(llvm::equal(OX->getProtocols(), OY->getProtocols()));
     auto TAs = getCommonTypes(Ctx, OX->getTypeArgsAsWritten(),
                               OY->getTypeArgsAsWritten());
@@ -12565,16 +12618,16 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
         OX->isKindOfTypeAsWritten() && OY->isKindOfTypeAsWritten());
   }
   case Type::ConstantMatrix: {
-    const auto *MX = cast<ConstantMatrixType>(X.Ty),
-               *MY = cast<ConstantMatrixType>(Y.Ty);
+    const auto *MX = cast<ConstantMatrixType>(X),
+               *MY = cast<ConstantMatrixType>(Y);
     assert(MX->getNumRows() == MY->getNumRows());
     assert(MX->getNumColumns() == MY->getNumColumns());
     return Ctx.getConstantMatrixType(getCommonElementType(Ctx, MX, MY),
                                      MX->getNumRows(), MX->getNumColumns());
   }
   case Type::DependentSizedMatrix: {
-    const auto *MX = cast<DependentSizedMatrixType>(X.Ty),
-               *MY = cast<DependentSizedMatrixType>(Y.Ty);
+    const auto *MX = cast<DependentSizedMatrixType>(X),
+               *MY = cast<DependentSizedMatrixType>(Y);
     assert(Ctx.hasSameExpr(MX->getRowExpr(), MY->getRowExpr()));
     assert(Ctx.hasSameExpr(MX->getColumnExpr(), MY->getColumnExpr()));
     return Ctx.getDependentSizedMatrixType(
@@ -12582,62 +12635,62 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
         MX->getColumnExpr(), getCommonAttrLoc(MX, MY));
   }
   case Type::Vector: {
-    const auto *VX = cast<VectorType>(X.Ty), *VY = cast<VectorType>(Y.Ty);
+    const auto *VX = cast<VectorType>(X), *VY = cast<VectorType>(Y);
     assert(VX->getNumElements() == VY->getNumElements());
     assert(VX->getVectorKind() == VY->getVectorKind());
     return Ctx.getVectorType(getCommonElementType(Ctx, VX, VY),
                              VX->getNumElements(), VX->getVectorKind());
   }
   case Type::ExtVector: {
-    const auto *VX = cast<ExtVectorType>(X.Ty), *VY = cast<ExtVectorType>(Y.Ty);
+    const auto *VX = cast<ExtVectorType>(X), *VY = cast<ExtVectorType>(Y);
     assert(VX->getNumElements() == VY->getNumElements());
     return Ctx.getExtVectorType(getCommonElementType(Ctx, VX, VY),
                                 VX->getNumElements());
   }
   case Type::DependentSizedExtVector: {
-    const auto *VX = cast<DependentSizedExtVectorType>(X.Ty),
-               *VY = cast<DependentSizedExtVectorType>(Y.Ty);
+    const auto *VX = cast<DependentSizedExtVectorType>(X),
+               *VY = cast<DependentSizedExtVectorType>(Y);
     return Ctx.getDependentSizedExtVectorType(getCommonElementType(Ctx, VX, VY),
                                               getCommonSizeExpr(Ctx, VX, VY),
                                               getCommonAttrLoc(VX, VY));
   }
   case Type::DependentVector: {
-    const auto *VX = cast<DependentVectorType>(X.Ty),
-               *VY = cast<DependentVectorType>(Y.Ty);
+    const auto *VX = cast<DependentVectorType>(X),
+               *VY = cast<DependentVectorType>(Y);
     assert(VX->getVectorKind() == VY->getVectorKind());
     return Ctx.getDependentVectorType(
         getCommonElementType(Ctx, VX, VY), getCommonSizeExpr(Ctx, VX, VY),
         getCommonAttrLoc(VX, VY), VX->getVectorKind());
   }
   case Type::InjectedClassName: {
-    const auto *IX = cast<InjectedClassNameType>(X.Ty),
-               *IY = cast<InjectedClassNameType>(Y.Ty);
+    const auto *IX = cast<InjectedClassNameType>(X),
+               *IY = cast<InjectedClassNameType>(Y);
     return Ctx.getInjectedClassNameType(
         getCommonDeclChecked(IX->getDecl(), IY->getDecl()),
         Ctx.getCommonSugaredType(IX->getInjectedSpecializationType(),
                                  IY->getInjectedSpecializationType()));
   }
   case Type::TemplateSpecialization: {
-    const auto *TX = cast<TemplateSpecializationType>(X.Ty),
-               *TY = cast<TemplateSpecializationType>(Y.Ty);
+    const auto *TX = cast<TemplateSpecializationType>(X),
+               *TY = cast<TemplateSpecializationType>(Y);
     auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(),
                                          TY->template_arguments());
     return Ctx.getTemplateSpecializationType(
-        ::getCommonTemplateName(Ctx, TX->getTemplateName(),
-                                TY->getTemplateName()),
-        As, TX->getCanonicalTypeInternal());
+        ::getCommonTemplateNameChecked(Ctx, TX->getTemplateName(),
+                                       TY->getTemplateName()),
+        As, X->getCanonicalTypeInternal());
   }
   case Type::DependentName: {
-    const auto *NX = cast<DependentNameType>(X.Ty),
-               *NY = cast<DependentNameType>(Y.Ty);
+    const auto *NX = cast<DependentNameType>(X),
+               *NY = cast<DependentNameType>(Y);
     assert(NX->getIdentifier() == NY->getIdentifier());
     return Ctx.getDependentNameType(
         getCommonTypeKeyword(NX, NY), getCommonNNS(Ctx, NX, NY),
         NX->getIdentifier(), NX->getCanonicalTypeInternal());
   }
   case Type::DependentTemplateSpecialization: {
-    const auto *TX = cast<DependentTemplateSpecializationType>(X.Ty),
-               *TY = cast<DependentTemplateSpecializationType>(Y.Ty);
+    const auto *TX = cast<DependentTemplateSpecializationType>(X),
+               *TY = cast<DependentTemplateSpecializationType>(Y);
     assert(TX->getIdentifier() == TY->getIdentifier());
     auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(),
                                          TY->template_arguments());
@@ -12646,8 +12699,8 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
         TX->getIdentifier(), As);
   }
   case Type::UnaryTransform: {
-    const auto *TX = cast<UnaryTransformType>(X.Ty),
-               *TY = cast<UnaryTransformType>(Y.Ty);
+    const auto *TX = cast<UnaryTransformType>(X),
+               *TY = cast<UnaryTransformType>(Y);
     assert(TX->getUTTKind() == TY->getUTTKind());
     return Ctx.getUnaryTransformType(
         Ctx.getCommonSugaredType(TX->getBaseType(), TY->getBaseType()),
@@ -12656,23 +12709,23 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
         TX->getUTTKind());
   }
   case Type::PackExpansion: {
-    const auto *PX = cast<PackExpansionType>(X.Ty),
-               *PY = cast<PackExpansionType>(Y.Ty);
+    const auto *PX = cast<PackExpansionType>(X),
+               *PY = cast<PackExpansionType>(Y);
     assert(PX->getNumExpansions() == PY->getNumExpansions());
     return Ctx.getPackExpansionType(
         Ctx.getCommonSugaredType(PX->getPattern(), PY->getPattern()),
         PX->getNumExpansions(), false);
   }
   case Type::Pipe: {
-    const auto *PX = cast<PipeType>(X.Ty), *PY = cast<PipeType>(Y.Ty);
+    const auto *PX = cast<PipeType>(X), *PY = cast<PipeType>(Y);
     assert(PX->isReadOnly() == PY->isReadOnly());
     auto MP = PX->isReadOnly() ? &ASTContext::getReadPipeType
                                : &ASTContext::getWritePipeType;
     return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY));
   }
   case Type::TemplateTypeParm: {
-    const auto *TX = cast<TemplateTypeParmType>(X.Ty),
-               *TY = cast<TemplateTypeParmType>(Y.Ty);
+    const auto *TX = cast<TemplateTypeParmType>(X),
+               *TY = cast<TemplateTypeParmType>(Y);
     assert(TX->getDepth() == TY->getDepth());
     assert(TX->getIndex() == TY->getIndex());
     assert(TX->isParameterPack() == TY->isParameterPack());
@@ -12684,31 +12737,208 @@ static QualType getCommonType(ASTContext &Ctx, SplitQualType &X,
   llvm_unreachable("Unknown Type Class");
 }
 
-static auto unwrapSugar(SplitQualType &T) {
+static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
+                                       const Type *Y,
+                                       SplitQualType Underlying) {
+  Type::TypeClass TC = X->getTypeClass();
+  if (TC != Y->getTypeClass())
+    return QualType();
+  switch (TC) {
+#define UNEXPECTED_TYPE(Class, Kind)                                           \
+  case Type::Class:                                                            \
+    llvm_unreachable("Unexpected " Kind ": " #Class);
+#define TYPE(Class, Base)
+#define DEPENDENT_TYPE(Class, Base) UNEXPECTED_TYPE(Class, "dependent")
+#include "clang/AST/TypeNodes.inc"
+
+#define CANONICAL_TYPE(Class) UNEXPECTED_TYPE(Class, "canonical")
+    CANONICAL_TYPE(Atomic)
+    CANONICAL_TYPE(BitInt)
+    CANONICAL_TYPE(BlockPointer)
+    CANONICAL_TYPE(Builtin)
+    CANONICAL_TYPE(Complex)
+    CANONICAL_TYPE(ConstantArray)
+    CANONICAL_TYPE(ConstantMatrix)
+    CANONICAL_TYPE(Enum)
+    CANONICAL_TYPE(ExtVector)
+    CANONICAL_TYPE(FunctionNoProto)
+    CANONICAL_TYPE(FunctionProto)
+    CANONICAL_TYPE(IncompleteArray)
+    CANONICAL_TYPE(LValueReference)
+    CANONICAL_TYPE(MemberPointer)
+    CANONICAL_TYPE(ObjCInterface)
+    CANONICAL_TYPE(ObjCObject)
+    CANONICAL_TYPE(ObjCObjectPointer)
+    CANONICAL_TYPE(Pipe)
+    CANONICAL_TYPE(Pointer)
+    CANONICAL_TYPE(Record)
+    CANONICAL_TYPE(RValueReference)
+    CANONICAL_TYPE(VariableArray)
+    CANONICAL_TYPE(Vector)
+#undef CANONICAL_TYPE
+
+#undef UNEXPECTED_TYPE
+
+  case Type::Adjusted: {
+    const auto *AX = cast<AdjustedType>(X), *AY = cast<AdjustedType>(Y);
+    QualType OX = AX->getOriginalType(), OY = AY->getOriginalType();
+    if (!Ctx.hasSameType(OX, OY))
+      return QualType();
+    // FIXME: It's inefficient to have to unify the original types.
+    return Ctx.getAdjustedType(Ctx.getCommonSugaredType(OX, OY),
+                               Ctx.getQualifiedType(Underlying));
+  }
+  case Type::Decayed: {
+    const auto *DX = cast<DecayedType>(X), *DY = cast<DecayedType>(Y);
+    QualType OX = DX->getOriginalType(), OY = DY->getOriginalType();
+    if (!Ctx.hasSameType(OX, OY))
+      return QualType();
+    // FIXME: It's inefficient to have to unify the original types.
+    return Ctx.getDecayedType(Ctx.getCommonSugaredType(OX, OY),
+                              Ctx.getQualifiedType(Underlying));
+  }
+  case Type::Attributed: {
+    const auto *AX = cast<AttributedType>(X), *AY = cast<AttributedType>(Y);
+    AttributedType::Kind Kind = AX->getAttrKind();
+    if (Kind != AY->getAttrKind())
+      return QualType();
+    QualType MX = AX->getModifiedType(), MY = AY->getModifiedType();
+    if (!Ctx.hasSameType(MX, MY))
+      return QualType();
+    // FIXME: It's inefficient to have to unify the modified types.
+    return Ctx.getAttributedType(Kind, Ctx.getCommonSugaredType(MX, MY),
+                                 Ctx.getQualifiedType(Underlying));
+  }
+  case Type::BTFTagAttributed: {
+    const auto *BX = cast<BTFTagAttributedType>(X);
+    const BTFTypeTagAttr *AX = BX->getAttr();
+    // The attribute is not uniqued, so just compare the tag.
+    if (AX->getBTFTypeTag() !=
+        cast<BTFTagAttributedType>(Y)->getAttr()->getBTFTypeTag())
+      return QualType();
+    return Ctx.getBTFTagAttributedType(AX, Ctx.getQualifiedType(Underlying));
+  }
+  case Type::Auto: {
+    const auto *AX = cast<AutoType>(X), *AY = cast<AutoType>(Y);
+
+    AutoTypeKeyword KW = AX->getKeyword();
+    if (KW != AY->getKeyword())
+      return QualType();
+
+    ConceptDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(),
+                                      AY->getTypeConstraintConcept());
+    SmallVector<TemplateArgument, 8> As;
+    if (CD &&
+        getCommonTemplateArguments(Ctx, As, AX->getTypeConstraintArguments(),
+                                   AY->getTypeConstraintArguments()))
+      CD = nullptr; // The arguments differ, so make it unconstrained.
+
+    // Both auto types can't be dependent, otherwise they wouldn't have been
+    // sugar. This implies they can't contain unexpanded packs either.
+    return Ctx.getAutoType(Ctx.getQualifiedType(Underlying), AX->getKeyword(),
+                           /*IsDependent=*/false, /*IsPack=*/false, CD, As);
+  }
+  case Type::Decltype:
+    return QualType();
+  case Type::DeducedTemplateSpecialization:
+    // FIXME: Try to merge these.
+    return QualType();
+
+  case Type::Elaborated: {
+    const auto *EX = cast<ElaboratedType>(X), *EY = cast<ElaboratedType>(Y);
+    return Ctx.getElaboratedType(
+        ::getCommonTypeKeyword(EX, EY), ::getCommonNNS(Ctx, EX, EY),
+        Ctx.getQualifiedType(Underlying),
+        ::getCommonDecl(EX->getOwnedTagDecl(), EY->getOwnedTagDecl()));
+  }
+  case Type::MacroQualified: {
+    const auto *MX = cast<MacroQualifiedType>(X),
+               *MY = cast<MacroQualifiedType>(Y);
+    const IdentifierInfo *IX = MX->getMacroIdentifier();
+    if (IX != MY->getMacroIdentifier())
+      return QualType();
+    return Ctx.getMacroQualifiedType(Ctx.getQualifiedType(Underlying), IX);
+  }
+  case Type::SubstTemplateTypeParm: {
+    const auto *SX = cast<SubstTemplateTypeParmType>(X),
+               *SY = cast<SubstTemplateTypeParmType>(Y);
+    const TemplateTypeParmType *PX = SX->getReplacedParameter();
+    if (PX != SY->getReplacedParameter())
+      return QualType();
+
+    return Ctx.getSubstTemplateTypeParmType(PX,
+                                            Ctx.getQualifiedType(Underlying));
+  }
+  case Type::ObjCTypeParam:
+    // FIXME: Try to merge these.
+    return QualType();
+  case Type::Paren:
+    return Ctx.getParenType(Ctx.getQualifiedType(Underlying));
+
+  case Type::TemplateSpecialization: {
+    const auto *TX = cast<TemplateSpecializationType>(X),
+               *TY = cast<TemplateSpecializationType>(Y);
+    TemplateName CTN = ::getCommonTemplateName(Ctx, TX->getTemplateName(),
+                                               TY->getTemplateName());
+    if (!CTN.getAsVoidPointer())
+      return QualType();
+    SmallVector<TemplateArgument, 8> Args;
+    if (getCommonTemplateArguments(Ctx, Args, TX->template_arguments(),
+                                   TY->template_arguments()))
+      return QualType();
+    return Ctx.getTemplateSpecializationType(CTN, Args,
+                                             Ctx.getQualifiedType(Underlying));
+  }
+  case Type::Typedef: {
+    const auto *TX = cast<TypedefType>(X), *TY = cast<TypedefType>(Y);
+    const TypedefNameDecl *CD = ::getCommonDecl(TX->getDecl(), TY->getDecl());
+    if (!CD)
+      return QualType();
+    return Ctx.getTypedefType(CD, Ctx.getQualifiedType(Underlying));
+  }
+  case Type::TypeOf:
+    return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying));
+  case Type::TypeOfExpr:
+    return QualType();
+
+  case Type::UnaryTransform: {
+    const auto *UX = cast<UnaryTransformType>(X),
+               *UY = cast<UnaryTransformType>(Y);
+    UnaryTransformType::UTTKind KX = UX->getUTTKind();
+    if (KX != UY->getUTTKind())
+      return QualType();
+    QualType BX = UX->getBaseType(), BY = UY->getBaseType();
+    if (!Ctx.hasSameType(BX, BY))
+      return QualType();
+    // FIXME: It's inefficient to have to unify the base types.
+    return Ctx.getUnaryTransformType(Ctx.getCommonSugaredType(BX, BY),
+                                     Ctx.getQualifiedType(Underlying), KX);
+  }
+  case Type::Using: {
+    const auto *UX = cast<UsingType>(X), *UY = cast<UsingType>(Y);
+    const UsingShadowDecl *CD =
+        ::getCommonDecl(UX->getFoundDecl(), UY->getFoundDecl());
+    if (!CD)
+      return QualType();
+    return Ctx.getUsingType(CD, Ctx.getQualifiedType(Underlying));
+  }
+  }
+  llvm_unreachable("Unhandled Type Class");
+}
+
+static auto unwrapSugar(SplitQualType &T, Qualifiers &QTotal) {
   SmallVector<SplitQualType, 8> R;
   while (true) {
+    QTotal += T.Quals;
     QualType NT = T.Ty->getLocallyUnqualifiedSingleStepDesugaredType();
     if (NT == QualType(T.Ty, 0))
       break;
-    SplitQualType SplitNT = NT.split();
-    SplitNT.Quals += T.Quals;
     R.push_back(T);
-    T = SplitNT;
+    T = NT.split();
   }
   return R;
 }
 
-static bool removeDifferentTopLevelSugar(SplitQualType &SX, SplitQualType &SY) {
-  auto Xs = ::unwrapSugar(SX), Ys = ::unwrapSugar(SY);
-  if (SX.Ty != SY.Ty)
-    return true;
-  while (!Xs.empty() && !Ys.empty() && Xs.back().Ty == Ys.back().Ty) {
-    SX = Xs.pop_back_val();
-    SY = Ys.pop_back_val();
-  }
-  return false;
-}
-
 QualType ASTContext::getCommonSugaredType(QualType X, QualType Y,
                                           bool Unqualified) {
   assert(Unqualified ? hasSameUnqualifiedType(X, Y) : hasSameType(X, Y));
@@ -12722,15 +12952,52 @@ QualType ASTContext::getCommonSugaredType(QualType X, QualType Y,
   }
 
   SplitQualType SX = X.split(), SY = Y.split();
-  if (::removeDifferentTopLevelSugar(SX, SY))
-    SX.Ty = ::getCommonType(*this, SX, SY).getTypePtr();
-
+  Qualifiers QX, QY;
+  // Desugar SX and SY, setting the sugar and qualifiers aside into Xs and Ys,
+  // until we reach their underlying "canonical nodes". Note these are not
+  // necessarily canonical types, as they may still have sugared properties.
+  // QX and QY will store the sum of all qualifiers in Xs and Ys respectively.
+  auto Xs = ::unwrapSugar(SX, QX), Ys = ::unwrapSugar(SY, QY);
+  if (SX.Ty != SY.Ty) {
+    // The canonical nodes differ. Build a common canonical node out of the two,
+    // unifying their sugar. This may recurse back here.
+    SX.Ty =
+        ::getCommonNonSugarTypeNode(*this, SX.Ty, QX, SY.Ty, QY).getTypePtr();
+  } else {
+    // The canonical nodes were identical: We may have desugared too much.
+    // Add any common sugar back in.
+    while (!Xs.empty() && !Ys.empty() && Xs.back().Ty == Ys.back().Ty) {
+      QX -= SX.Quals;
+      QY -= SY.Quals;
+      SX = Xs.pop_back_val();
+      SY = Ys.pop_back_val();
+    }
+  }
   if (Unqualified)
-    SX.Quals = Qualifiers::removeCommonQualifiers(SX.Quals, SY.Quals);
+    QX = Qualifiers::removeCommonQualifiers(QX, QY);
   else
-    assert(SX.Quals == SY.Quals);
+    assert(QX == QY);
+
+  // Even though the remaining sugar nodes in Xs and Ys differ, some may be
+  // related. Walk up these nodes, unifying them and adding the result.
+  while (!Xs.empty() && !Ys.empty()) {
+    auto Underlying = SplitQualType(
+        SX.Ty, Qualifiers::removeCommonQualifiers(SX.Quals, SY.Quals));
+    SX = Xs.pop_back_val();
+    SY = Ys.pop_back_val();
+    SX.Ty = ::getCommonSugarTypeNode(*this, SX.Ty, SY.Ty, Underlying)
+                .getTypePtrOrNull();
+    // Stop at the first pair which is unrelated.
+    if (!SX.Ty) {
+      SX.Ty = Underlying.Ty;
+      break;
+    }
+    QX -= Underlying.Quals;
+  };
 
-  QualType R = getQualifiedType(SX);
+  // Add back the missing accumulated qualifiers, which were stripped off
+  // with the sugar nodes we could not unify.
+  QualType R = getQualifiedType(SX.Ty, QX);
   assert(Unqualified ? hasSameUnqualifiedType(R, X) : hasSameType(R, X));
   return R;
 }
index e0a154b..ed507fd 100644 (file)
@@ -6281,8 +6281,9 @@ bool Sema::CheckTemplateArgument(TypeSourceInfo *ArgInfo) {
   assert(ArgInfo && "invalid TypeSourceInfo");
   QualType Arg = ArgInfo->getType();
   SourceRange SR = ArgInfo->getTypeLoc().getSourceRange();
+  QualType CanonArg = Context.getCanonicalType(Arg);
 
-  if (Arg->isVariablyModifiedType()) {
+  if (CanonArg->isVariablyModifiedType()) {
     return Diag(SR.getBegin(), diag::err_variably_modified_template_arg) << Arg;
   } else if (Context.hasSameUnqualifiedType(Arg, Context.OverloadTy)) {
     return Diag(SR.getBegin(), diag::err_template_arg_overload_type) << SR;
@@ -6295,9 +6296,9 @@ bool Sema::CheckTemplateArgument(TypeSourceInfo *ArgInfo) {
   //
   // C++11 allows these, and even in C++03 we allow them as an extension with
   // a warning.
-  if (LangOpts.CPlusPlus11 || Arg->hasUnnamedOrLocalType()) {
+  if (LangOpts.CPlusPlus11 || CanonArg->hasUnnamedOrLocalType()) {
     UnnamedLocalNoLinkageFinder Finder(*this, SR);
-    (void)Finder.Visit(Context.getCanonicalType(Arg));
+    (void)Finder.Visit(CanonArg);
   }
 
   return false;
index 0e07a15..82f6d94 100644 (file)
@@ -3825,13 +3825,11 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(
     //   - If A is an array type, the pointer type produced by the
     //     array-to-pointer standard conversion (4.2) is used in place of
     //     A for type deduction; otherwise,
-    if (ArgType->isArrayType())
-      ArgType = S.Context.getArrayDecayedType(ArgType);
     //   - If A is a function type, the pointer type produced by the
     //     function-to-pointer standard conversion (4.3) is used in place
     //     of A for type deduction; otherwise,
-    else if (ArgType->isFunctionType())
-      ArgType = S.Context.getPointerType(ArgType);
+    if (ArgType->canDecayToPointerType())
+      ArgType = S.Context.getDecayedType(ArgType);
     else {
       // - If A is a cv-qualified type, the top level cv-qualifiers of A's
       //   type are ignored for type deduction.
index 8c18d8a..7ae3a87 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -fenable-matrix
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -fenable-matrix -triple i686-pc-win32
 
 enum class N {};
 
@@ -38,3 +38,77 @@ N t6 = A3() ? X1() : Y1(); // expected-error {{vector condition type 'A3' (vecto
 N t7 = X4() + Y4(); // expected-error {{rvalue of type 'B4'}}
 N t8 = X4() * Y4(); // expected-error {{rvalue of type 'B4'}}
 N t9 = X5() * Y5(); // expected-error {{rvalue of type 'A4 __attribute__((matrix_type(3, 3)))'}}
+
+template <class T> struct S1 {
+  template <class U> struct S2 {};
+};
+
+N t10 = 0 ? S1<X1>() : S1<Y1>(); // expected-error {{from 'S1<B1>' (aka 'S1<int>')}}
+N t11 = 0 ? S1<X1>::S2<X2>() : S1<Y1>::S2<Y2>(); // expected-error {{from 'S1<int>::S2<B2>' (aka 'S2<void>')}}
+
+template <class T> using Al = S1<T>;
+
+N t12 = 0 ? Al<X1>() : Al<Y1>(); // expected-error {{from 'Al<B1>' (aka 'S1<int>')}}
+
+#define AS1 __attribute__((address_space(1)))
+#define AS2 __attribute__((address_space(1)))
+using AS1X1 = AS1 B1;
+using AS1Y1 = AS1 B1;
+using AS2Y1 = AS2 B1;
+N t13 = 0 ? (AS1X1){} : (AS1Y1){}; // expected-error {{rvalue of type 'AS1 B1' (aka '__attribute__((address_space(1))) int')}}
+N t14 = 0 ? (AS1X1){} : (AS2Y1){}; // expected-error {{rvalue of type '__attribute__((address_space(1))) B1' (aka '__attribute__((address_space(1))) int')}}
+
+using FX1 = X1 ();
+using FY1 = Y1 ();
+N t15 = 0 ? (FX1*){} : (FY1*){}; // expected-error {{rvalue of type 'B1 (*)()' (aka 'int (*)()')}}
+
+struct SS1 {};
+using SB1 = SS1;
+using SX1 = SB1;
+using SY1 = SB1;
+
+using MFX1 = X1 SX1::*();
+using MFY1 = Y1 SY1::*();
+
+N t16 = 0 ? (MFX1*){} : (MFY1*){}; // expected-error {{rvalue of type 'B1 SB1::*(*)()'}}
+
+N t17 = 0 ? (FX1 SX1::*){} : (FY1 SY1::*){}; // expected-error {{rvalue of type 'B1 (SB1::*)() __attribute__((thiscall))'}}
+
+N t18 = 0 ? (__typeof(X1*)){} : (__typeof(Y1*)){}; // expected-error {{rvalue of type 'typeof(B1 *)' (aka 'int *')}}
+
+struct Enums {
+  enum X : B1;
+  enum Y : ::B1;
+};
+using EnumsB = Enums;
+using EnumsX = EnumsB;
+using EnumsY = EnumsB;
+
+N t19 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::Y)){};
+// expected-error@-1 {{rvalue of type 'B1' (aka 'int')}}
+
+N t20 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::X)){};
+// expected-error@-1 {{rvalue of type '__underlying_type(Enums::X)' (aka 'int')}}
+
+using SBTF1 = SS1 [[clang::btf_type_tag("1")]];
+using SBTF2 = ::SS1 [[clang::btf_type_tag("1")]];
+using SBTF3 = ::SS1 [[clang::btf_type_tag("2")]];
+
+N t21 = 0 ? (SBTF1){} : (SBTF3){}; // expected-error {{from 'SS1'}}
+N t22 = 0 ? (SBTF1){} : (SBTF2){}; // expected-error {{from 'SS1 btf_type_tag(1)' (aka 'SS1')}}
+
+using QX = const SB1 *;
+using QY = const ::SB1 *;
+N t23 = 0 ? (QX){} : (QY){}; // expected-error {{rvalue of type 'const SB1 *' (aka 'const SS1 *')}}
+
+template <class T> using Alias = short;
+N t24 = 0 ? (Alias<X1>){} : (Alias<Y1>){}; // expected-error {{rvalue of type 'Alias<B1>' (aka 'short')}}
+N t25 = 0 ? (Alias<X1>){} : (Alias<X2>){}; // expected-error {{rvalue of type 'short'}}
+
+template <class T, class U> concept C1 = true;
+template <class T, class U> concept C2 = true;
+C1<X1> auto t26_1 = (SB1){};
+C1<X2> auto t26_2 = (::SB1){};
+C2<X2> auto t26_3 = (::SB1){};
+N t26 = 0 ? t26_1 : t26_2; // expected-error {{from 'SB1' (aka 'SS1')}}
+N t27 = 0 ? t26_1 : t26_3; // expected-error {{from 'SB1' (aka 'SS1')}}
index 12d39e0..c865c1a 100644 (file)
@@ -190,6 +190,14 @@ void g() {
 }
 } // namespace test7
 
+namespace test8 {
+template <class T> void foo(T);
+void test(int a) {
+    char n[a];
+    foo(n);
+}
+} // namespace test8
+
 // Verify that we can deduce enum-typed arguments correctly.
 namespace test14 {
   enum E { E0, E1 };