Index: clang/include/clang/AST/OperationKinds.def =================================================================== --- clang/include/clang/AST/OperationKinds.def +++ clang/include/clang/AST/OperationKinds.def @@ -83,6 +83,13 @@ /// void () noexcept -> void () CAST_OPERATION(NoOp) +/// CK_ArrayBound - A conversion from a pointer/reference to an array that +/// changes the bounds of the array. +/// int (*)[] -> int (*)[5] +/// char (&)[5] -> char (&)[] +/// float (*)[5] -> float (*)[4] +CAST_OPERATION(ArrayBound) + /// CK_BaseToDerived - A conversion from a C++ class pointer/reference /// to a derived class pointer/reference. /// B *b = static_cast(a); Index: clang/include/clang/Sema/Initialization.h =================================================================== --- clang/include/clang/Sema/Initialization.h +++ clang/include/clang/Sema/Initialization.h @@ -827,6 +827,11 @@ /// final stage of class copy-initialization. SK_FinalCopy, + /// Change the bounds of an array type somewhere in the pointer + /// decomposition of a type (i.e. not necessarily only at the outermost + /// level). + SK_ArrayBoundConversion, + /// Perform a user-defined conversion, either via a conversion /// function or via a constructor. SK_UserConversion, @@ -1280,6 +1285,10 @@ void AddQualificationConversionStep(QualType Ty, ExprValueKind Category); + /// Add a new step that performs an array boundconversion to the + /// given type. + void AddArrayBoundConversionStep(QualType Ty); + /// Add a new step that performs a function reference conversion to the /// given type. void AddFunctionReferenceConversionStep(QualType Ty); Index: clang/include/clang/Sema/Overload.h =================================================================== --- clang/include/clang/Sema/Overload.h +++ clang/include/clang/Sema/Overload.h @@ -282,6 +282,10 @@ /// Objective-C lifetime (for automatic reference counting). unsigned QualificationIncludesObjCLifetime : 1; + // Whether the qualification conversion involves a change in array bounds + // (and is therefore CK_ArrayBound, rather than CK_NoOp). + unsigned QualificationIncludesArrayBound : 1; + /// IncompatibleObjC - Whether this is an Objective-C conversion /// that we should warn about (if we actually use it). unsigned IncompatibleObjC : 1; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3431,7 +3431,8 @@ CXXCastPath &BasePath, bool IgnoreBaseAccess); bool IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle, bool &ObjCLifetimeConversion); + bool CStyle, bool &ObjCLifetimeConversion, + bool *ArrayBoundConversion = nullptr); bool IsFunctionConversion(QualType FromType, QualType ToType, QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); @@ -11670,12 +11671,16 @@ /// binding a reference of type T1 to it, as determined when evaluating /// whether T1 is reference-compatible with T2. enum ReferenceConversions { - Qualification = 0x1, - NestedQualification = 0x2, - Function = 0x4, - DerivedToBase = 0x8, - ObjC = 0x10, - ObjCLifetime = 0x20, + // At-most one of the following can be set. + Function = 1 << 0, + DerivedToBase = 1 << 1, + ArrayBound = 1 << 2, + ObjC = 1 << 3, + + // The following are combinable. + Qualification = 1 << 4, + NestedQualification = 1 << 5, + ObjCLifetime = 1 << 6, LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/ObjCLifetime) }; Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -1795,6 +1795,7 @@ case CK_Dependent: case CK_LValueToRValue: case CK_NoOp: + case CK_ArrayBound: case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: case CK_PointerToBoolean: Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -7796,6 +7796,7 @@ return DerivedSuccess(AtomicVal, E); } + case CK_ArrayBound: case CK_NoOp: case CK_UserDefinedConversion: return StmtVisitorTy::Visit(E->getSubExpr()); @@ -13199,6 +13200,7 @@ case CK_UserDefinedConversion: case CK_LValueToRValue: case CK_AtomicToNonAtomic: + case CK_ArrayBound: case CK_NoOp: case CK_LValueToRValueBitCast: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -13446,6 +13448,7 @@ return Success(Result, E); } + case CK_ArrayBound: case CK_NoOp: case CK_LValueToRValue: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -13929,6 +13932,7 @@ case CK_LValueToRValue: case CK_AtomicToNonAtomic: + case CK_ArrayBound: case CK_NoOp: case CK_LValueToRValueBitCast: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -15388,6 +15392,7 @@ case CK_LValueToRValue: case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: + case CK_ArrayBound: case CK_NoOp: case CK_IntegralToBoolean: case CK_IntegralCast: Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -112,6 +112,7 @@ case CK_FunctionToPointerDecay: case CK_NonAtomicToAtomic: case CK_NoOp: + case CK_ArrayBound: case CK_UserDefinedConversion: return this->Visit(SubExpr); Index: clang/lib/Analysis/ThreadSafetyCommon.cpp =================================================================== --- clang/lib/Analysis/ThreadSafetyCommon.cpp +++ clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -574,6 +574,7 @@ // return new (Arena) til::Load(E0); } case CK_NoOp: + case CK_ArrayBound: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: case CK_ArrayToPointerDecay: Index: clang/lib/CodeGen/CGDecl.cpp =================================================================== --- clang/lib/CodeGen/CGDecl.cpp +++ clang/lib/CodeGen/CGDecl.cpp @@ -686,6 +686,7 @@ switch (castExpr->getCastKind()) { // Look through casts that don't require representation changes. case CK_NoOp: + case CK_ArrayBound: case CK_BitCast: case CK_BlockPointerToObjCPointerCast: needsCast = true; Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -1086,6 +1086,7 @@ // Non-converting casts (but not C's implicit conversion from void*). case CK_BitCast: case CK_NoOp: + case CK_ArrayBound: case CK_AddressSpaceConversion: if (auto PtrTy = CE->getSubExpr()->getType()->getAs()) { if (PtrTy->getPointeeType()->isVoidType()) @@ -4688,6 +4689,13 @@ case CK_LValueToRValue: return EmitLValue(E->getSubExpr()); + case CK_ArrayBound: { + Address Addr = EmitLValue(E->getSubExpr()).getAddress(*this); + unsigned AddrSpace = Addr.getPointer()->getType()->getPointerAddressSpace(); + llvm::Type *PtrTy = ConvertType(E->getType())->getPointerTo(AddrSpace); + return MakeAddrLValue(Builder.CreateBitCast(Addr, PtrTy), E->getType()); + } + case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { const auto *DerivedClassTy = Index: clang/lib/CodeGen/CGExprAgg.cpp =================================================================== --- clang/lib/CodeGen/CGExprAgg.cpp +++ clang/lib/CodeGen/CGExprAgg.cpp @@ -858,6 +858,7 @@ case CK_LValueBitCast: llvm_unreachable("should not be emitting lvalue bitcast as rvalue"); + case CK_ArrayBound: // FIXME: check this is right categorization. case CK_Dependent: case CK_BitCast: case CK_ArrayToPointerDecay: @@ -1394,6 +1395,7 @@ switch (CE->getCastKind()) { // No-ops. case CK_NoOp: + case CK_ArrayBound: case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_BitCast: Index: clang/lib/CodeGen/CGExprComplex.cpp =================================================================== --- clang/lib/CodeGen/CGExprComplex.cpp +++ clang/lib/CodeGen/CGExprComplex.cpp @@ -484,6 +484,7 @@ } case CK_BitCast: + case CK_ArrayBound: case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: Index: clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- clang/lib/CodeGen/CGExprConstant.cpp +++ clang/lib/CodeGen/CGExprConstant.cpp @@ -1126,6 +1126,7 @@ // These don't need to be handled here because Evaluate knows how to // evaluate them in the cases where they can be folded. case CK_BitCast: + case CK_ArrayBound: // FIXME: /Does/ Evaluate know? case CK_ToVoid: case CK_Dynamic: case CK_LValueBitCast: Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -1991,6 +1991,7 @@ case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: + case CK_ArrayBound: case CK_BitCast: { Value *Src = Visit(const_cast(E)); llvm::Type *SrcTy = Src->getType(); Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -3046,6 +3046,7 @@ // These operations preserve a block type. case CK_NoOp: + case CK_ArrayBound: case CK_BitCast: return shouldEmitSeparateBlockRetain(cast->getSubExpr()); @@ -3167,6 +3168,7 @@ case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: + case CK_ArrayBound: case CK_BitCast: { llvm::Type *resultType = CGF.ConvertType(e->getType()); assert(e->getSubExpr()->getType()->hasPointerRepresentation()); Index: clang/lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1039,6 +1039,7 @@ needsCast = true; break; + case CK_ArrayBound: case CK_Dependent: case CK_BitCast: case CK_LValueBitCast: Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -14275,6 +14275,7 @@ default: break; case CK_NoOp: + case CK_ArrayBound: return getBaseAlignmentAndOffsetFromLValue(From, Ctx); case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { @@ -14362,6 +14363,7 @@ default: break; case CK_NoOp: + case CK_ArrayBound: return getBaseAlignmentAndOffsetFromPtr(From, Ctx); case CK_ArrayToPointerDecay: return getBaseAlignmentAndOffsetFromLValue(From, Ctx); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -18237,6 +18237,7 @@ // can be found. switch (ICE->getCastKind()) { case CK_NoOp: + case CK_ArrayBound: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: { ExprResult Sub = Rebuild(ICE->getSubExpr()); Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -4551,6 +4551,9 @@ From->getType()->getPointeeType().getAddressSpace()) CK = CK_AddressSpaceConversion; + if (SCS.QualificationIncludesArrayBound) + CK = CK_ArrayBound; + From = ImpCastExprToType(From, ToType.getNonLValueExprType(Context), CK, VK, /*BasePath=*/nullptr, CCK) .get(); Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -3586,6 +3586,7 @@ return ACC_bottom; case CK_NoOp: + case CK_ArrayBound: case CK_LValueToRValue: case CK_BitCast: case CK_CPointerToObjCPointerCast: Index: clang/lib/Sema/SemaInit.cpp =================================================================== --- clang/lib/Sema/SemaInit.cpp +++ clang/lib/Sema/SemaInit.cpp @@ -3465,6 +3465,7 @@ case SK_FinalCopy: case SK_ExtraneousCopyToTemporary: case SK_UserConversion: + case SK_ArrayBoundConversion: case SK_QualificationConversionRValue: case SK_QualificationConversionXValue: case SK_QualificationConversionLValue: @@ -3654,6 +3655,13 @@ Steps.push_back(S); } +void InitializationSequence::AddArrayBoundConversionStep(QualType Ty) { + Step S; + S.Kind = SK_ArrayBoundConversion; + S.Type = Ty; + Steps.push_back(S); +} + void InitializationSequence::AddAtomicConversionStep(QualType Ty) { Step S; S.Kind = SK_AtomicConversion; @@ -4816,7 +4824,7 @@ Sema::ReferenceConversions::ObjC)) { // If we're converting the pointee, add any qualifiers first; // these qualifiers must all be top-level, so just convert to "cv1 T2". - if (RefConv & (Sema::ReferenceConversions::Qualification)) + if (RefConv & Sema::ReferenceConversions::Qualification) Sequence.AddQualificationConversionStep( S.Context.getQualifiedType(T2, T1Quals), Initializer->getValueKind()); @@ -4824,6 +4832,8 @@ Sequence.AddDerivedToBaseCastStep(cv1T1, VK_LValue); else Sequence.AddObjCObjectConversionStep(cv1T1); + } else if (RefConv & Sema::ReferenceConversions::ArrayBound) { + Sequence.AddArrayBoundConversionStep(cv1T1); } else if (RefConv & Sema::ReferenceConversions::Qualification) { // Perform a (possibly multi-level) qualification conversion. Sequence.AddQualificationConversionStep(cv1T1, @@ -5443,6 +5453,7 @@ case CK_BitCast: case CK_LValueBitCast: case CK_NoOp: + case CK_ArrayBound: return isInvalidICRSource(C, ce->getSubExpr(), isAddressOf, isWeakAccess); case CK_ArrayToPointerDecay: @@ -7253,6 +7264,7 @@ // We assume that casts to 'bool' do not preserve enough information to // retain a local object. case CK_NoOp: + case CK_ArrayBound: case CK_BitCast: case CK_BaseToDerived: case CK_DerivedToBase: @@ -8112,6 +8124,7 @@ case SK_FinalCopy: case SK_ExtraneousCopyToTemporary: case SK_UserConversion: + case SK_ArrayBoundConversion: case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: @@ -8380,6 +8393,13 @@ break; } + case SK_ArrayBoundConversion: + // Perform an array bound conversion. + // FIXME: If pointer to array not LValue? + CurInit = S.ImpCastExprToType(CurInit.get(), Step->Type, CK_ArrayBound, + VK_LValue); + break; + case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: { @@ -9648,6 +9668,10 @@ OS << "user-defined conversion via " << *S->Function.Function; break; + case SK_ArrayBoundConversion: + OS << "array bound conversion"; + break; + case SK_QualificationConversionRValue: OS << "qualification conversion (rvalue)"; break; Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -196,6 +196,7 @@ Third = ICK_Identity; DeprecatedStringLiteralToCharPtr = false; QualificationIncludesObjCLifetime = false; + QualificationIncludesArrayBound = false; ReferenceBinding = false; DirectBinding = false; IsLvalueReference = true; @@ -289,6 +290,7 @@ Converted = ICE->getSubExpr(); continue; + case CK_ArrayBound: // FIXME: Right categorization? default: return Converted; } @@ -1956,15 +1958,17 @@ // The third conversion can be a function pointer conversion or a // qualification conversion (C++ [conv.fctptr], [conv.qual]). - bool ObjCLifetimeConversion; + bool ObjCLifetimeConversion, ArrayBoundConversion = false; if (S.IsFunctionConversion(FromType, ToType, FromType)) { // Function pointer conversions (removing 'noexcept') including removal of // 'noreturn' (Clang extension). SCS.Third = ICK_Function_Conversion; } else if (S.IsQualificationConversion(FromType, ToType, CStyle, - ObjCLifetimeConversion)) { + ObjCLifetimeConversion, + &ArrayBoundConversion)) { SCS.Third = ICK_Qualification; SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion; + SCS.QualificationIncludesArrayBound = ArrayBoundConversion; FromType = ToType; } else { // No conversion required @@ -3189,6 +3193,7 @@ /// Specifically, check whether any change between the qualifiers of \p /// FromType and \p ToType is permissible, given knowledge about whether every /// outer layer is const-qualified. +/// This does not handle C++20 bounded->unbounded array conversion static bool isQualificationConversionStep(QualType FromType, QualType ToType, bool CStyle, bool IsTopLevel, bool &PreviousToQualsIncludeConst, @@ -3258,9 +3263,10 @@ /// \param ObjCLifetimeConversion Output parameter that will be set to indicate /// when the qualification conversion involves a change in the Objective-C /// object lifetime. -bool -Sema::IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle, bool &ObjCLifetimeConversion) { +bool Sema::IsQualificationConversion(QualType FromType, QualType ToType, + bool CStyle, bool &ObjCLifetimeConversion, + // FIXME: should this be CastKind? ref? + bool *ArrayBoundConversion) { FromType = Context.getCanonicalType(FromType); ToType = Context.getCanonicalType(ToType); ObjCLifetimeConversion = false; @@ -3274,13 +3280,29 @@ // A conversion can add cv-qualifiers at levels other than the first // in multi-level pointers, subject to the following rules: [...] bool PreviousToQualsIncludeConst = true; - bool UnwrappedAnyPointer = false; - while (Context.UnwrapSimilarTypes(FromType, ToType)) { + bool UnwrappedAnything = false; + for (;;) { + if (getLangOpts().CPlusPlus20) + // C++20, p0388, T1 can be unbounded array and T2 bounded array + if (auto AT1 = Context.getAsArrayType(ToType)) + if (isa(AT1)) + if (auto AT2 = Context.getAsArrayType(FromType)) + if (isa(AT2)) { + ToType = AT1->getElementType(); + FromType = AT2->getElementType(); + if (ArrayBoundConversion) + *ArrayBoundConversion = true; + UnwrappedAnything = true; + } + + if (!Context.UnwrapSimilarTypes(FromType, ToType)) + break; + if (!isQualificationConversionStep( - FromType, ToType, CStyle, !UnwrappedAnyPointer, + FromType, ToType, CStyle, !UnwrappedAnything, PreviousToQualsIncludeConst, ObjCLifetimeConversion)) return false; - UnwrappedAnyPointer = true; + UnwrappedAnything = true; } // We are left with FromType and ToType being the pointee types @@ -3288,7 +3310,7 @@ // of times. If we unwrapped any pointers, and if FromType and // ToType have the same unqualified type (since we checked // qualifiers above), then this is a qualification conversion. - return UnwrappedAnyPointer && Context.hasSameUnqualifiedType(FromType,ToType); + return UnwrappedAnything && Context.hasSameUnqualifiedType(FromType,ToType); } /// - Determine whether this is a conversion from a scalar type to an @@ -3315,6 +3337,8 @@ SCS.Third = InnerSCS.Third; SCS.QualificationIncludesObjCLifetime = InnerSCS.QualificationIncludesObjCLifetime; + SCS.QualificationIncludesArrayBound = + InnerSCS.QualificationIncludesArrayBound; SCS.setToType(2, InnerSCS.getToType(2)); return true; } @@ -4216,6 +4240,7 @@ } while (S.Context.UnwrapSimilarTypes(T1, T2)) { + // FIXME: Array bound conversion comparison? // Within each iteration of the loop, we check the qualifiers to // determine if this still looks like a qualification // conversion. Then, if all is well, we unwrap one more level of @@ -4580,6 +4605,17 @@ T1 = withoutUnaligned(Context, T1); T2 = withoutUnaligned(Context, T2); + if (getLangOpts().CPlusPlus20) + // C++20, p0388, T1 can be incomplete array and T2 bounded array + // FIXME: We have canonical types here, so may not need getAsArrayType? + if (auto AT1 = Context.getAsArrayType(T1)) + if (isa(AT1)) + if (auto AT2 = Context.getAsArrayType(T2)) + if (isa(AT2)) { + T1 = AT1->getElementType(); + T2 = AT2->getElementType(); + Conv |= ReferenceConversions::ArrayBound; + } // If we find a qualifier mismatch, the types are not reference-compatible, // but are still be reference-related if they're similar. bool ObjCLifetimeConversion = false; @@ -4600,9 +4636,13 @@ // At this point, if the types are reference-related, we must either have the // same inner type (ignoring qualifiers), or must have already worked out how // to convert the referent. - return (ConvertedReferent || Context.hasSameUnqualifiedType(T1, T2)) - ? Ref_Compatible - : Ref_Incompatible; + if (ConvertedReferent) + return Ref_Compatible; + + if (Context.hasSameUnqualifiedType(T1, T2)) + return Ref_Compatible; + + return Ref_Incompatible; } /// Look for a user-defined conversion to a value reference-compatible @@ -7218,6 +7258,7 @@ /// Objective-C pointer to another. /// /// \returns true if the conversion is allowable, false otherwise. +// FIXME: Should this return arraybound flag? static bool isAllowableExplicitConversion(Sema &S, QualType ConvType, QualType ToType, bool AllowObjCPointerConversion) { Index: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -377,6 +377,7 @@ handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; } + case CK_ArrayBound: // FIXME: is this the best categorization? case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -351,6 +351,7 @@ case CK_ArrayToPointerDecay: case CK_IntegralToPointer: case CK_NoOp: + case CK_ArrayBound: case CK_BitCast: { const Expr *SE = CE->getSubExpr(); Optional Val = getConstantVal(SE); Index: clang/test/CXX/drs/dr3xx.cpp =================================================================== --- clang/test/CXX/drs/dr3xx.cpp +++ clang/test/CXX/drs/dr3xx.cpp @@ -373,10 +373,19 @@ q = p; // ok q2 = p; // ok r = p; // expected-error {{incompatible}} - s = p; // expected-error {{incompatible}} (for now) + s = p; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif t = p; // expected-error {{incompatible}} - s = q; // expected-error {{incompatible}} - s = q2; // expected-error {{incompatible}} + s = q; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif + s = q2; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif s = t; // ok, adding const t = s; // expected-error {{discards qualifiers}} (void) const_cast

(q); Index: clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 %s -triple %itanium_abi_triple -std=c++20 -emit-llvm -O2 -o - | FileCheck %s + +// p0388 conversions to unbounded array +// dcl.init.list/3 + +namespace One { +int ga[1]; + +// CHECK-LABEL: @_ZN3One5frob1Ev +// CHECK-NEXT: entry: +// CHECK-NEXT: ret [0 x i32]* bitcast ([1 x i32]* @_ZN3One2gaE to [0 x i32]*) +auto &frob1() { + int(&r1)[] = ga; + + return r1; +} + +// CHECK-LABEL: @_ZN3One5frob2ERA1_i +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast [1 x i32]* %arp to [0 x i32]* +// CHECK-NEXT: ret [0 x i32]* %0 +auto &frob2(int (&arp)[1]) { + int(&r2)[] = arp; + + return r2; +} +} // namespace One Index: clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify %s + +// p0388 conversions to unbounded array +// dcl.init.list/3 + +#if __cplusplus >= 202002 +// expected-no-diagnostics +#endif + +namespace One { +int ga[1]; + +auto &frob1() { + int(&r1)[] = ga; +#if __cplusplus < 202002 + // expected-error@-2{{cannot bind to a value of unrelated type}} +#endif + + return r1; +} + +auto &frob2(int (&arp)[1]) { + int(&r2)[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{cannot bind to a value of unrelated type}} +#endif + + return r2; +} +} // namespace One + +namespace Two { +int ga[1]; + +auto *frob1() { + int(*r1)[] = &ga; +#if __cplusplus < 202002 + // expected-error@-2{{with an rvalue of type}} +#endif + + return r1; +} + +auto *frob2(int (*arp)[1]) { + int(*r2)[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{with an lvalue of type}} +#endif + + return r2; +} +} // namespace Two + +namespace Four { +using Mat = int[1][2]; +using Inc = int[][2]; +Mat *ga[1]; + +auto *frob1() { + Inc *(*r1)[] = &ga; +#if __cplusplus < 202002 + // expected-error@-2{{with an rvalue of type}} +#endif + + return r1; +} + +auto *frob2(Mat *(*arp)[1]) { + Inc *(*r2)[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{with an lvalue of type}} +#endif + + return r2; +} + +} // namespace Four