Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11624,4 +11624,24 @@ "pointers to WebAssembly reference types are illegal">; def err_wasm_addrof_reference : Error< "cannot take address of WebAssembly reference">; +def err_typecheck_wasm_table_must_have_zero_length : Error< + "only zero-length WebAssembly tables are currently supported">; +def err_wasm_table_in_function : Error< + "WebAssembly table cannot be declared within a function">; +def err_wasm_table_as_function_argument : Error< + "cannot use WebAssembly table as a function argument">; +def err_wasm_table_invalid_uett_operand : Error< + "invalid application of '%0' to WebAssembly table">; +def err_wasm_cast_table : Error< + "cannot cast WebAssembly table">; +def err_wasm_table_conditional_expression : Error< + "cannot use WebAssembly tables as the 2nd or 3rd operands of a conditional expression">; +def err_wasm_table_assignment : Error< + "cannot assign WebAssembly table to a variable">; +def err_wasm_table_return : Error< + "cannot return a WebAssembly table">; +def err_wasm_table_must_be_static : Error< + "WebAssembly table must be static">; +def err_wasm_multidimensional_ref_array : Error< + "multi-dimensional arrays of WebAssembly references are illegal">; } // end of sema component. Index: clang/lib/Sema/SemaCast.cpp =================================================================== --- clang/lib/Sema/SemaCast.cpp +++ clang/lib/Sema/SemaCast.cpp @@ -2895,6 +2895,14 @@ return; QualType SrcType = SrcExpr.get()->getType(); + if (SrcType->isPointerType() && + SrcType->getPointeeType()->isWebAssemblyReferenceType()) { + Self.Diag(OpRange.getBegin(), diag::err_wasm_cast_table) + << SrcExpr.get()->getSourceRange(); + SrcExpr = ExprError(); + return; + } + assert(!SrcType->isPlaceholderType()); checkAddressSpaceCast(SrcType, DestType); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -7594,6 +7594,18 @@ } } + // WebAssembly tables are always in address space 1 (wasm_var). Don't apply + // address space if the table has local storage (semantic checks elsewhere + // will produce an error anyway). + if (const Type *ElTy = NewVD->getType()->getArrayElementTypeNoTypeQual()) { + if (ElTy && ElTy->isWebAssemblyReferenceType() && + !NewVD->hasLocalStorage()) { + QualType Type = Context.getAddrSpaceQualType( + NewVD->getType(), Context.getLangASForBuiltinAddressSpace(1)); + NewVD->setType(Type); + } + } + // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); @@ -8354,6 +8366,29 @@ } } + // WebAssembly tables must be static with a zero length and can't be + // declared within functions. + if (Context.getTargetInfo().getTriple().isWasm() && T->isArrayType() && + T->getArrayElementTypeNoTypeQual()->isWebAssemblyReferenceType()) { + if (getCurScope()->isFunctionScope()) { + Diag(NewVD->getLocation(), diag::err_wasm_table_in_function); + NewVD->setInvalidDecl(); + return; + } + if (NewVD->getStorageClass() != SC_Static) { + Diag(NewVD->getLocation(), diag::err_wasm_table_must_be_static); + NewVD->setInvalidDecl(); + return; + } + const auto *ATy = dyn_cast(T.getTypePtr()); + if (!ATy || ATy->getSize().getSExtValue() != 0) { + Diag(NewVD->getLocation(), + diag::err_typecheck_wasm_table_must_have_zero_length); + NewVD->setInvalidDecl(); + return; + } + } + bool isVM = T->isVariablyModifiedType(); if (isVM || NewVD->hasAttr() || NewVD->hasAttr()) @@ -10319,6 +10354,18 @@ } } } + // WebAssembly tables can't be used as function parameters. + if (Context.getTargetInfo().getTriple().isWasm()) { + while (PT->isPointerType()) { + PT = PT->getPointeeType(); + if (PT->isWebAssemblyReferenceType() || + PT->getBaseElementTypeUnsafe()->isWebAssemblyReferenceType()) { + Diag(Param->getTypeSpecStartLoc(), + diag::err_wasm_table_as_function_argument); + D.setInvalidType(); + } + } + } } // Here we have an function template explicit specialization at class scope. @@ -12516,6 +12563,16 @@ return; } + // WebAssembly tables can't be used to initialise a variable. + if (Init && !Init->getType().isNull() && Init->getType()->isArrayType() && + Init->getType() + ->getArrayElementTypeNoTypeQual() + ->isWebAssemblyReferenceType()) { + Diag(Init->getExprLoc(), diag::err_wasm_table_assignment); + VDecl->setInvalidDecl(); + return; + } + // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. if (VDecl->getType()->isUndeducedType()) { // Attempt typo correction early so that the type of the init expression can Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -4262,6 +4262,17 @@ E->getSourceRange(), ExprKind)) return false; + // WebAssembly tables are always illegal operands to unary expressions and + // type traits. + if (Context.getTargetInfo().getTriple().isWasm()) { + const Type *ElTy = E->getType()->getArrayElementTypeNoTypeQual(); + if (ElTy && ElTy->isWebAssemblyReferenceType()) { + Diag(E->getExprLoc(), diag::err_wasm_table_invalid_uett_operand) + << getTraitSpelling(ExprKind); + return true; + } + } + // 'alignof' applied to an expression only requires the base element type of // the expression to be complete. 'sizeof' requires the expression's type to // be complete (and will attempt to complete it if it's an array of unknown @@ -4377,6 +4388,17 @@ return true; } + // WebAssembly tables are always illegal operands to unary expressions and + // type traits. + if (Context.getTargetInfo().getTriple().isWasm()) { + const Type *ElTy = ExprType->getBaseElementTypeUnsafe(); + if (ElTy != ExprType.getTypePtr() && ElTy->isWebAssemblyReferenceType()) { + Diag(OpLoc, diag::err_wasm_table_invalid_uett_operand) + << getTraitSpelling(ExprKind); + return true; + } + } + if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange, ExprKind)) return true; @@ -5787,6 +5809,7 @@ if (!ResultType.hasQualifiers()) VK = VK_PRValue; } else if (!ResultType->isDependentType() && + !ResultType->isWebAssemblyReferenceType() && RequireCompleteSizedType( LLoc, ResultType, diag::err_subscript_incomplete_or_sizeless_type, BaseExpr)) @@ -7063,6 +7086,21 @@ TheCall->setType(FuncT->getCallResultType(Context)); TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); + // WebAssembly tables can't be used as arguments. + if (Context.getTargetInfo().getTriple().isWasm()) { + for (const Expr *Arg : Args) { + if (Arg) { + if (const Type *ElTy = + Arg->getType()->getArrayElementTypeNoTypeQual()) { + if (ElTy && ElTy->isWebAssemblyReferenceType()) { + return ExprError(Diag(Arg->getExprLoc(), + diag::err_wasm_table_as_function_argument)); + } + } + } + } + } + if (Proto) { if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, IsExecConfig)) @@ -8613,6 +8651,14 @@ QualType LHSTy = LHS.get()->getType(); QualType RHSTy = RHS.get()->getType(); + if ((LHSTy->isPointerType() && + LHSTy->getPointeeType()->isWebAssemblyReferenceType()) || + (RHSTy->isPointerType() && + RHSTy->getPointeeType()->isWebAssemblyReferenceType())) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } // Diagnose attempts to convert between __ibm128, __float128 and long double // where such conversions currently can't be handled. @@ -12316,6 +12362,12 @@ (RHSType->isArithmeticType() || RHSType->isEnumeralType())) return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); + if ((LHSType->isPointerType() && + LHSType->getPointeeType()->isWebAssemblyReferenceType()) || + (RHSType->isPointerType() && + RHSType->getPointeeType()->isWebAssemblyReferenceType())) + return InvalidOperands(Loc, LHS, RHS); + const Expr::NullPointerConstantKind LHSNullKind = LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); const Expr::NullPointerConstantKind RHSNullKind = @@ -13210,6 +13262,16 @@ if (EnumConstantInBoolContext) Diag(Loc, diag::warn_enum_constant_in_bool_context); + // WebAssembly tables can't be used with logical operators. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + if ((LHSTy->isArrayType() && + LHSTy->getArrayElementTypeNoTypeQual()->isWebAssemblyReferenceType()) || + (RHSTy->isArrayType() && + RHSTy->getArrayElementTypeNoTypeQual()->isWebAssemblyReferenceType())) { + return InvalidOperands(Loc, LHS, RHS); + } + // Diagnose cases where the user write a logical and/or but probably meant a // bitwise one. We do this when the LHS is a non-bool integer and the RHS // is a constant. @@ -13750,6 +13812,13 @@ return QualType(); } + // WebAssembly tables can't be used on RHS of an assignment expression. + if (RHSType->isArrayType() && + RHSType->getArrayElementTypeNoTypeQual()->isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_table_assignment); + return QualType(); + } + AssignConvertType ConvTy; if (CompoundType.isNull()) { Expr *RHSCheck = RHS.get(); @@ -15508,6 +15577,13 @@ resultType = Context.FloatTy; } + // WebAsembly tables can't be used in unary expressions. + if (resultType->isPointerType() && + resultType->getPointeeType()->isWebAssemblyReferenceType()) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + if (resultType->isDependentType()) break; if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -3971,6 +3971,16 @@ } else // If we don't have a function/method context, bail. return StmtError(); + if (RetValExp) { + if (RetValExp->getType()->isArrayType() && + RetValExp->getType() + ->getArrayElementTypeNoTypeQual() + ->isWebAssemblyReferenceType()) { + Diag(ReturnLoc, diag::err_wasm_table_return); + return StmtError(); + } + } + // C++1z: discarded return statements are not considered when deducing a // return type. if (ExprEvalContexts.back().isDiscardedStatementContext() && Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -2429,12 +2429,22 @@ } else { // C99 6.7.5.2p1: If the element type is an incomplete or function type, // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]()) - if (RequireCompleteSizedType(Loc, T, + if (!T->isWebAssemblyReferenceType() && + RequireCompleteSizedType(Loc, T, diag::err_array_incomplete_or_sizeless_type)) return QualType(); } - if (T->isSizelessType()) { + // Multi-dimensional arrays of WebAssembly references are not allowed. + if (Context.getTargetInfo().getTriple().isWasm() && T->isArrayType()) { + const Type *BaseTy = T->getBaseElementTypeUnsafe(); + if (BaseTy->isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_multidimensional_ref_array); + return QualType(); + } + } + + if (T->isSizelessType() && !T->isWebAssemblyReferenceType()) { Diag(Loc, diag::err_array_incomplete_or_sizeless_type) << 1 << T; return QualType(); } Index: clang/test/CodeGen/WebAssembly/table.c =================================================================== --- /dev/null +++ clang/test/CodeGen/WebAssembly/table.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s +// REQUIRES: webassembly-registered-target + +// CHECK: @table = internal addrspace(1) global [0 x ptr addrspace(10)] zeroinitializer, align 1 +static __externref_t table[0]; + +void use() { + // Ensure the table isn't discarded as unused. + table[0]; +} Index: clang/test/Sema/wasm-refs-and-tables.c =================================================================== --- clang/test/Sema/wasm-refs-and-tables.c +++ clang/test/Sema/wasm-refs-and-tables.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s +// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -Wno-unused-value -target-feature +reference-types %s // Note: As WebAssembly references are sizeless types, we don't exhaustively // test for cases covered by sizeless-1.c and similar tests. @@ -6,34 +6,35 @@ __externref_t *t1; // expected-error {{pointers to WebAssembly reference types are illegal}} __externref_t **t2; // expected-error {{pointers to WebAssembly reference types are illegal}} __externref_t ******t3; // expected-error {{pointers to WebAssembly reference types are illegal}} -static __externref_t t4[3]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t5[]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t6[] = {0}; // expected-error {{array has sizeless element type '__externref_t'}} -__externref_t t7[0]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t8[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} +static __externref_t t4[3]; // expected-error {{only zero-length WebAssembly tables are currently supported}} +static __externref_t t5[]; // expected-error {{only zero-length WebAssembly tables are currently supported}} +static __externref_t t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}} +__externref_t t7[0]; // expected-error {{WebAssembly table must be static}} +static __externref_t t8[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} -static __externref_t table[0]; // expected-error {{array has sizeless element type '__externref_t'}} +static __externref_t table[0]; +static __externref_t other_table[0] = {}; struct s { __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} - __externref_t f2[0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f3[]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f4[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} + __externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} __externref_t *f5; // expected-error {{pointers to WebAssembly reference types are illegal}} __externref_t ****f6; // expected-error {{pointers to WebAssembly reference types are illegal}} }; union u { __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} - __externref_t f2[0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f3[]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f4[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} + __externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} __externref_t *f5; // expected-error {{pointers to WebAssembly reference types are illegal}} __externref_t ****f6; // expected-error {{pointers to WebAssembly reference types are illegal}} }; -void illegal_argument_1(__externref_t table[]); // expected-error {{array has sizeless element type '__externref_t'}} -void illegal_argument_2(__externref_t table[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} +void illegal_argument_1(__externref_t table[]); // expected-error {{cannot use WebAssembly table as a function argument}} +void illegal_argument_2(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} void illegal_argument_3(__externref_t *table); // expected-error {{pointers to WebAssembly reference types are illegal}} void illegal_argument_4(__externref_t ***table); // expected-error {{pointers to WebAssembly reference types are illegal}} @@ -49,18 +50,40 @@ (__externref_t ****)(&foo); // expected-error {{pointers to WebAssembly reference types are illegal}} sizeof(ref); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} sizeof(__externref_t); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} - sizeof(__externref_t[0]); // expected-error {{array has sizeless element type '__externref_t'}} - sizeof(__externref_t[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} + sizeof(__externref_t[0]); // expected-error {{invalid application of 'sizeof' to WebAssembly table}} + sizeof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} sizeof(__externref_t *); // expected-error {{pointers to WebAssembly reference types are illegal}} sizeof(__externref_t ***); // expected-error {{pointers to WebAssembly reference types are illegal}}; // expected-warning@+1 {{'_Alignof' applied to an expression is a GNU extension}} _Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} _Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - _Alignof(__externref_t[]); // expected-error {{array has sizeless element type '__externref_t'}} - _Alignof(__externref_t[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} + _Alignof(__externref_t[]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} _Alignof(__externref_t *); // expected-error {{pointers to WebAssembly reference types are illegal}} _Alignof(__externref_t ***); // expected-error {{pointers to WebAssembly reference types are illegal}}; varargs(1, ref); // expected-error {{cannot pass expression of type '__externref_t' to variadic function}} + __externref_t lt1[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + static __externref_t lt2[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + static __externref_t lt3[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are illegal}} + illegal_argument_1(table); // expected-error {{cannot use WebAssembly table as a function argument}} + varargs(1, table); // expected-error {{cannot use WebAssembly table as a function argument}} + table == 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} + 1 >= table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} + !table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}} + 1 && table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} + table || 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} + 1 ? table : table; // expected-error {{cannot use WebAssembly tables as the 2nd or 3rd operands of a conditional expression}} + (void *)table; // expected-error {{cannot cast WebAssembly table}} + void *u; + u = table; // expected-error {{cannot assign WebAssembly table to a variable}} + void *v = table; // expected-error {{cannot assign WebAssembly table to a variable}} + + table[0]; + table[0] = ref; return ref; } + +void *ret_void_ptr() { + return table; // expected-error {{cannot return a WebAssembly table}} +}