diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1357,6 +1357,7 @@ CanQualType getDecayedType(CanQualType T) const { return CanQualType::CreateUnsafe(getDecayedType((QualType) T)); } + QualType getDecayedType(QualType T, QualType Decayed) const; /// Return the uniqued reference to the atomic type for the specified /// type. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3329,6 +3329,26 @@ return QualType(AT, 0); } +QualType ASTContext::getDecayedType(QualType T, QualType Decayed) const { + 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); +} + QualType ASTContext::getDecayedType(QualType T) const { assert((T->isArrayType() || T->isFunctionType()) && "T does not decay"); @@ -3349,23 +3369,7 @@ 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 @@ -12107,7 +12111,8 @@ static Decl *getCommonDecl(Decl *X, Decl *Y) { if (X == Y) return X; - assert(declaresSameEntity(X, Y)); + if (!declaresSameEntity(X, Y)) + return nullptr; for (const Decl *DX : X->redecls()) { // If we reach Y before reaching the canonical Decl, that means X is older. if (DX == Y) @@ -12127,6 +12132,13 @@ const_cast(cast_or_null(Y)))); } +template ::value, bool> = true> +T *getCommonDeclChecked(T *X, T *Y) { + return cast(getCommonDecl(const_cast(cast(X)), + const_cast(cast(Y)))); +} + static TemplateName getCommonTemplateName(ASTContext &Ctx, TemplateName X, TemplateName Y) { if (X.getAsVoidPointer() == Y.getAsVoidPointer()) @@ -12135,11 +12147,19 @@ // 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 getCommonTypeArray(ASTContext &Ctx, ArrayRef Xs, ArrayRef Ys, bool Unqualified = false) { @@ -12159,14 +12179,44 @@ 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()), 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(), + NExpY = Y.getNumTemplateExpansions(); + return TemplateArgument( + CTN, + NExpX && NExpY ? Optional(std::min(*NExpX, *NExpY)) : None); + } default: // FIXME: Handle the other argument kinds. return X; @@ -12174,12 +12224,28 @@ llvm_unreachable(""); } +static bool getCommonTemplateArguments(ASTContext &Ctx, + SmallVectorImpl &R, + ArrayRef Xs, + ArrayRef 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 X, - ArrayRef Y) { - SmallVector R(X.size()); - for (size_t I = 0; I < R.size(); ++I) - R[I] = getCommonTemplateArgument(Ctx, X[I], Y[I]); + ArrayRef Xs, + ArrayRef Ys) { + SmallVector R; + bool Different = getCommonTemplateArguments(Ctx, R, Xs, Ys); + assert(!Different); + (void)Different; return R; } @@ -12306,7 +12372,8 @@ llvm_unreachable("invalid ExceptionSpecificationType"); } -static QualType getCommonType(ASTContext &Ctx, const Type *X, const Type *Y) { +static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, + const Type *Y) { Type::TypeClass TC = X->getTypeClass(); assert(TC == Y->getTypeClass()); switch (TC) { @@ -12352,8 +12419,8 @@ return Ctx.getAutoType(QualType(), AX->getKeyword(), AX->isInstantiationDependentType(), AX->containsUnexpandedParameterPack(), - getCommonDecl(AX->getTypeConstraintConcept(), - AY->getTypeConstraintConcept()), + getCommonDeclChecked(AX->getTypeConstraintConcept(), + AY->getTypeConstraintConcept()), As); } case Type::IncompleteArray: { @@ -12525,7 +12592,7 @@ const auto *IX = cast(X), *IY = cast(Y); return Ctx.getInjectedClassNameType( - getCommonDecl(IX->getDecl(), IY->getDecl()), + getCommonDeclChecked(IX->getDecl(), IY->getDecl()), Ctx.getCommonSugaredType(IX->getInjectedSpecializationType(), IY->getInjectedSpecializationType())); } @@ -12535,9 +12602,9 @@ 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(X), @@ -12585,31 +12652,212 @@ 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(X), *AY = cast(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(X), *DY = cast(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(X), *AY = cast(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(X); + const BTFTypeTagAttr *AX = BX->getAttr(); + // The attribute is not uniqued, so just compare the tag. + if (AX->getBTFTypeTag() != + cast(Y)->getAttr()->getBTFTypeTag()) + return QualType(); + return Ctx.getBTFTagAttributedType(AX, Ctx.getQualifiedType(Underlying)); + } + case Type::Auto: { + const auto *AX = cast(X), *AY = cast(Y); + + AutoTypeKeyword KW = AX->getKeyword(); + if (KW != AY->getKeyword()) + return QualType(); + + ConceptDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(), + AY->getTypeConstraintConcept()); + SmallVector 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(X), *EY = cast(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(X), + *MY = cast(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(X), + *SY = cast(Y); + auto PackIndex = SX->getPackIndex(); + if (PackIndex != SY->getPackIndex()) + return QualType(); + + const TemplateTypeParmType *PX = SX->getReplacedParameter(); + if (PX != SY->getReplacedParameter()) + return QualType(); + + return Ctx.getSubstTemplateTypeParmType( + PX, Ctx.getQualifiedType(Underlying), PackIndex); + } + 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(X), + *TY = cast(Y); + TemplateName CTN = ::getCommonTemplateName(Ctx, TX->getTemplateName(), + TY->getTemplateName()); + if (!CTN.getAsVoidPointer()) + return QualType(); + SmallVector As; + if (getCommonTemplateArguments(Ctx, As, TX->template_arguments(), + TY->template_arguments())) + return QualType(); + return Ctx.getTemplateSpecializationType(CTN, As, + Ctx.getQualifiedType(Underlying)); + } + case Type::Typedef: { + const auto *TX = cast(X), *TY = cast(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(X), + *UY = cast(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(X), *UY = cast(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 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)); @@ -12623,15 +12871,38 @@ } SplitQualType SX = X.split(), SY = Y.split(); - if (::removeDifferentTopLevelSugar(SX, SY)) - SX.Ty = ::getCommonType(*this, SX.Ty, SY.Ty).getTypePtr(); - + Qualifiers QX, QY; + auto Xs = ::unwrapSugar(SX, QX), Ys = ::unwrapSugar(SY, QY); + if (SX.Ty != SY.Ty) { + SX.Ty = ::getCommonNonSugarTypeNode(*this, SX.Ty, SY.Ty).getTypePtr(); + } else { + 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); + + 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(); + if (!SX.Ty) { + SX.Ty = Underlying.Ty; + break; + } + QX -= Underlying.Quals; + }; - QualType R = getQualifiedType(SX); + QualType R = getQualifiedType(SX.Ty, QX); assert(Unqualified ? hasSameUnqualifiedType(R, X) : hasSameType(R, X)); return R; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3776,13 +3776,11 @@ // - 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. diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp --- a/clang/test/SemaCXX/sugar-common-types.cpp +++ b/clang/test/SemaCXX/sugar-common-types.cpp @@ -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 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 struct S1 { + template struct S2 {}; +}; + +N t10 = 0 ? S1() : S1(); // expected-error {{from 'S1' (aka 'S1')}} +N t11 = 0 ? S1::S2() : S1::S2(); // expected-error {{from 'S1::S2' (aka 'S2')}} + +template using Al = S1; + +N t12 = 0 ? Al() : Al(); // expected-error {{from 'Al' (aka 'S1')}} + +#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 using Alias = short; +N t24 = 0 ? (Alias){} : (Alias){}; // expected-error {{rvalue of type 'Alias' (aka 'short')}} +N t25 = 0 ? (Alias){} : (Alias){}; // expected-error {{rvalue of type 'short'}} + +template concept C1 = true; +template concept C2 = true; +C1 auto t26_1 = (SB1){}; +C1 auto t26_2 = (::SB1){}; +C2 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')}}