무효가 아닌 메소드 컴파일에서 누락 된 리턴 문


189

void아닌 메소드return 문 이 누락 되어 코드가 여전히 컴파일 되는 상황이 발생했습니다 . while 루프 이후의 명령문은 도달 할 수 없으며 (데드 코드) 절대 실행되지 않습니다. 그러나 컴파일러가 왜 무언가를 반환하는 것에 대해 경고하지 않습니까? 아니면 왜 언어를 사용하여 무한 루프를 가지고 있고 아무것도 반환하지 않는 비 무효 메소드를 가질 수 있습니까?

public int doNotReturnAnything() {
    while(true) {
        //do something
    }
    //no return statement
}

while 루프에 break 문 (조건문 하나)을 추가하면 컴파일러는 악명 높은 오류 ( Method does not return a valueEclipse 및 Not all code paths return a valueVisual Studio) 에 대해 불평합니다 .

public int doNotReturnAnything() {
    while(true) {
        if(mustReturn) break;
        //do something
    }
    //no return statement
}

이것은 Java와 C # 모두에 해당됩니다.


3
좋은 질문. 나는 이것에 대한 이유에 관심이 있습니다.
Erik Schierboom 10

18
추측 : 그것은 무한 루프이므로 리턴 제어 흐름은 관련이 없습니까?
Lews Therin

5
"언어가 왜 무한 루프를 가지고 있고 아무것도 반환하지 않는 비 공백 메소드를 가질 수있게 했을까?" <-어리석은 것처럼 보이지만이 질문은 반대가 될 수 있습니다. 왜 이것이 허용되지 않습니까? 그건 그렇고 실제 코드입니까?
fge

3
Java의 경우이 답변 에서 좋은 설명을 찾을 수 있습니다 .
Keppil

4
다른 사람들이 설명했듯이, 컴파일러가 루프가 무한하다는 것을 알기에 충분히 똑똑하기 때문입니다. 컴파일러는 리턴 값의 누락을 허용 할뿐만 아니라 루프에 도달 할 수없는 이후의 내용을 알고 있기 때문에이를 적용 합니다. 적어도 Netbeans에서는 루프 뒤에 무언가unreachable statement 가 있는지 말 그대로 불평 합니다.
Supr

답변:


240

왜 언어를 사용하면 무한 루프를 가지고 있고 아무것도 반환하지 않는 비 공백 메소드를 가질 수 있습니까?

무효가 아닌 메소드의 규칙 은 리턴하는 모든 코드 경로가 값을 리턴해야 하며 해당 규칙이 프로그램에서 충족되는 것입니다. 리턴하는 0 개의 코드 경로 중 0은 값을 리턴합니다. 이 규칙은 "모든 비 공백 메소드는 리턴하는 코드 경로를 가져야합니다"는 아닙니다.

이를 통해 다음과 같은 스텁 메소드를 작성할 수 있습니다.

IEnumerator IEnumerable.GetEnumerator() 
{ 
    throw new NotImplementedException(); 
}

그것은 무효가 아닌 방법입니다. 그것은 갖는 인터페이스를 충족하기 위해 비 공간있어서한다. 그러나이 구현을 아무것도 반환하지 않기 때문에 불법으로 만드는 것은 어리석은 것처럼 보입니다.

당신의 방법은 때문에의 도달 끝 지점을 가지고 goto(A는 기억 while(true)쓰기 단지 더 즐거운 방법입니다 goto대신의) throw(의 또 다른 형태 인 goto) 관련이 없습니다.

컴파일러가 무언가를 반환하는 것에 대해 경고하지 않는 이유는 무엇입니까?

컴파일러는 코드가 잘못되었다는 좋은 증거가 없기 때문입니다. 누군가가 글을 썼는데 while(true), 그 일을 한 사람이 자신이하는 일을 알고있을 것 같습니다.

C #의 도달 가능성 분석에 대한 자세한 내용은 어디서 볼 수 있습니까?

주제에 대한 내 기사를 참조하십시오.

ATBG : 사실상 그리고 법적인 접근성

그리고 C # 사양을 읽는 것도 고려할 수 있습니다.


따라서이 코드가 컴파일 시간 오류를 발생시키는 이유 : public int doNotReturnAnything() { boolean flag = true; while (flag) { //Do something } //no return } 이 코드에는 중단 점이 없습니다. 이제 컴파일러가 코드를 잘못 알고있는 방법.
Sandeep Poonia

@ SandeepPoonia : 여기에 다른 답변을 읽고 싶을 수도 있습니다 ... 컴파일러는 특정 조건 만 감지 할 수 있습니다.
Daniel Hilgarth

9
@SandeepPoonia : 부울 플래그는 const가 아니므로 런타임에 변경할 수 있으므로 루프가 반드시 무한하지는 않습니다.
Schmuli

9
@SandeepPoonia : C #에서 스펙은 말한다 if, while, for, switch, 등상에서 동작 분지 구조 상수 컴파일러에서 무조건 분기로 취급된다. 상수 표현 의 정확한 정의 는 사양에 있습니다. 귀하의 질문에 대한 답변은 사양에 있습니다. 내 충고는 당신이 그것을 읽는 것입니다.
Eric Lippert

1
Every code path that returns must return a value가장 좋은 답변입니다. 두 질문 모두 명확하게 다룹니다. 감사
cPu1

38

Java 컴파일러는 도달 할 수없는 코드 ( while루프 후 코드)를 찾을 수있을 정도로 똑똑합니다.

그 이후로 도달 할 수없는 , 아무 소용이없는 추가에서 return이 문 (후 while종료)

조건부와 동일 if

public int get() {
   if(someBoolean) {   
     return 10;
   }
   else {
     return 5;
   }
   // there is no need of say, return 11 here;
}

부울 조건 someBooleantrue또는 로만 평가할 수 있으므로 해당 코드에 도달 할 수 없고 Java가 이에 대해 불평하지 않기 때문에 명시 적으로 after false를 제공 할 필요 가 없습니다.return if-else


4
나는 이것이 실제로 문제를 해결한다고 생각하지 않는다. 이 답변은 당신은 왜 필요하지 않습니다 return도달 할 수없는 코드에서 문을하지만, 당신이 필요로하지 않는 이유와는 아무 상관이없는 어떤 return 영업의 코드에서 문을.
밥슨

Java 컴파일러가 도달 할 수없는 코드를 찾을 수있을 정도로 똑똑하다면 (아래 루프 후 코드)이 아래 코드가 컴파일되는 이유는 모두 도달 할 수없는 코드를 가지고 있지만 if 메소드는 return 문이 필요하지만 while 메소드는 그렇지 않습니다. public int doNotReturnAnything() { if(true){ System.exit(1); } return 11; } public int doNotReturnAnything() { while(true){ System.exit(1); } return 11;// Compiler error: unreachable code }
Sandeep Poonia

@ SandeepPoonia : 컴파일러가 System.exit(1)프로그램을 죽일 것이라는 것을 알지 못하기 때문에 . 도달 할 수없는 특정 유형의 코드 만 감지 할 수 있습니다.
Daniel Hilgarth

@Daniel Hilgarth : Ok 컴파일러는 System.exit(1)프로그램을 죽일 지 몰라 , 우리는 any를 사용할 수 있습니다 return statement. 이제 컴파일러는를 인식합니다 return statements. 그리고 행동은 동일합니다. 반환에는 필요 if condition하지만 그렇지 않습니다 while.
Sandeep Poonia

1
다음과 같은 특별한 경우를 사용하지 않고 도달 할 수없는 섹션에 대한 좋은 데모while(true)
Matthew

17

컴파일러는 while루프가 실행을 멈추지 않으므로 메소드가 완료 return되지 않으므로 명령문이 필요하지 않다는 것을 알고 있습니다.


13

루프가 상수로 실행되고 있다고 가정하면 컴파일러는 무한 루프라는 것을 알고 있습니다.

변수를 사용하면 컴파일러가 규칙을 시행합니다.

이것은 컴파일되지 않습니다 :

// Define other methods and classes here
public int doNotReturnAnything() {
    var x = true;

    while(x == true) {
        //do something
    }
    //no return statement - won't compile
}

그러나 "무언가를하는 것"이 ​​어떤 식 으로든 x를 수정하는 것을 포함하지 않는다면 어떨까요? 컴파일러는 그것을 알아내는 것이 현명하지 않습니까? Bum :(
Lews Therin

아뇨-그렇게 보이지 않습니다.
Dave Bish

@Lews int를 리턴하는 것으로 표시된 메소드가 있지만 실제로 리턴하지 않는 경우, 컴파일러가이를 플래그로 지정하므로 메소드를 void로 표시하거나 의도하지 않은 경우 수정할 수 있습니다. 행동.
MikeFHay

@MikeFHay Yep, 논쟁의 여지가 없습니다.
Lews Therin

1
틀릴 수도 있지만 일부 디버거에서는 변수를 수정할 수 있습니다. 여기서 x는 코드로 수정되지 않고 JIT에 의해 최적화되지만 x를 false로 수정하고 메서드가 무언가를 반환해야합니다 (C # 디버거에서 허용되는 경우).
Maciej Piechotka

11

Java 스펙은이라는 개념을 정의합니다 Unreachable statements. 코드에 도달 할 수없는 진술을 할 수 없습니다 (컴파일 타임 오류입니다). while (true) 이후에는 return 문을 사용할 수 없습니다. 자바 문장. while(true);문 그러므로 당신이 필요하지 않은, 정의에 의해 다음 문에 도달 할 수 있습니다 return문을.

동안 참고하는 것이 앞뒤가 맞지 문제가 일반적인 경우 결정 불가능하다, 도달 할 수없는 문의 정의는 정지보다 더 엄격하다. 프로그램이 확실히 멈추지 않는 매우 구체적인 경우를 결정하고 있습니다. 컴파일러는 이론적으로 모든 무한 루프와 도달 할 수없는 명령문을 감지 할 수 없지만 스펙에 정의 된 특정 케이스 (예 : while(true)케이스) 를 감지해야합니다.


7

컴파일러는 while루프가 무한 하다는 것을 알기에 충분히 똑똑합니다 .

따라서 컴파일러는 당신을 생각할 수 없습니다. 그 코드를 작성 했는지 추측 할 수 없습니다 . 메소드의 리턴 값도 동일합니다. 메소드의 반환 값으로 아무것도하지 않으면 Java가 불평하지 않습니다.

따라서 귀하의 질문에 대답하십시오 :

컴파일러는 코드를 분석하고 실행 경로가 없어서 함수 끝에서 떨어지는 것으로 확인한 후 OK로 끝납니다.

무한 루프에 대한 합법적 인 이유가있을 수 있습니다. 예를 들어 많은 앱이 무한 메인 루프를 사용합니다. 다른 예는 요청을 무한정 기다릴 수있는 웹 서버입니다.


7

타입 이론에는, 다른 모든 타입 (!)의 서브 클래스 인 하부 타입 (bottom type) 이라고 불리는 것이 있으며 다른 것들 중에서 종료되지 않음을 나타내는 데 사용됩니다. 예외는 일종의 비 종료 유형으로 간주 될 수 있으며 일반 경로를 통해 종료되지 않습니다.

따라서 이론적 인 관점에서 종결되지 않는 이러한 문장은 하위 유형 인 int의 하위 유형을 반환하는 것으로 간주 될 수 있으므로 유형 관점에서 결국 반환 값을 얻습니다. 그리고 하나의 유형이 실제로 하나를 반환하지 않기 때문에 int를 포함하여 다른 모든 유형의 하위 클래스가 될 수 있다는 것은 전혀 의미가 없습니다.

어쨌든 명시 적 형식 이론을 통해, 컴파일러 (컴파일러 작성자)는 종료되지 않은 명령문 이후에 반환 값을 요청하는 것이 어리 석다는 것을 인식합니다. 해당 값이 필요할 수있는 경우는 없습니다. (종료되지 않을 것을 알고 있지만 무언가를 반환하기를 원할 때 컴파일러가 경고하도록하는 것이 좋을 수 있습니다.하지만 스타일 체커에 대해서는 왼쪽 표식이 더 좋습니다. 다른 이유로 (예 : 서브 클래 싱) 길을 찾지 만 실제로는 비 종료를 원합니다.)


6

함수가 적절한 값을 반환하지 않고 종료 될 수있는 상황은 없습니다. 따라서 컴파일러가 불평 할 것은 없습니다.


5

Visual Studio에는 반환 유형을 입력했는지 감지하는 스마트 엔진이 있으며 함수 / 방법에 return 문이 있어야합니다.

PHP에서와 같이 아무것도 반환하지 않으면 반환 유형이 true입니다. 아무것도 반환하지 않으면 컴파일러에서 1을 얻습니다.

현재

public int doNotReturnAnything() {
    while(true) {
        //do something
    }
    //no return statement
}

컴파일러는 진술 자체가 무한한 성질을 가지고 있으므로 고려하지 않아야 함을 알고 있습니다. php 컴파일러는 while의 표현으로 조건을 작성하면 자동으로 적용됩니다.

그러나 VS의 경우 스택의 오류를 반환하지 않습니다.


4

while 루프는 영원히 실행되므로 외부에 나오지 않습니다. 계속 실행됩니다. 따라서 {{}의 외부 부분에 도달 할 수 없으며 서면으로 답할 수있는 시점이 없습니다. 컴파일러는 도달 할 수있는 부분과 도달 할 수없는 부분을 파악할 수있을 정도로 지능적입니다.

예:

public int xyz(){
    boolean x=true;

    while(x==true){
        // do something  
    }

    // no return statement
}

변수 x의 값이 while 루프의 본문 내에서 수정 될 수 있으므로 위의 코드는 컴파일되지 않습니다. 따라서 while 루프의 외부 부분에 도달 할 수 있습니다! 따라서 컴파일러는 'no return statement found'라는 오류를 발생시킵니다.

컴파일러는 x의 값이 수정 될지 여부를 알아낼만큼 지능적이지 못합니다 (또는 게으른;). 이것이 모든 것을 지우기를 바랍니다.


7
실제로이 경우 컴파일러가 충분히 똑똑하다는 질문은 아닙니다. C # 2.0에서 컴파일러는 그것이 int x = 1; while(x * 0 == 0) { ... }무한 루프 라는 것을 알기에 충분히 똑똑 했지만, 스펙 은 루프 표현식이 상수 일 때 컴파일러가 제어 흐름을 공제해야 하고 스펙은 변수가없는 것으로 상수 표현식을 정의 한다고 말합니다 . 따라서 컴파일러는 너무 똑똑했다 . C # 3에서는 컴파일러가 사양과 일치하도록 만들었으며 그 이후로 이러한 종류의 표현식에 대해서는 의도적으로 덜 똑똑합니다.
Eric Lippert

4

"컴파일러가 왜 무언가를 반환하는 것에 대해 경고하지 않는가? 왜 언어를 통해 무한 루프를 가지고 있고 아무것도 반환하지 않는 비 공백 메소드를 가질 수 있을까?"

이 코드는 다른 모든 언어에서도 유효합니다 (아마도 Haskell! 제외). 첫 번째 가정은 우리가 일부러 코드를 "의도적으로"작성한다는 것입니다.

이 코드가 스레드로 사용되는 것처럼 완전히 유효 할 수있는 상황이 있습니다. 또는이 반환 Task<int>된 경우 반환 된 int 값을 기반으로 오류 검사를 수행 할 수 있습니다.


3

틀릴 수도 있지만 일부 디버거에서는 변수를 수정할 수 있습니다. 여기서 x는 코드로 수정되지 않고 JIT에 의해 최적화되지만 x를 false로 수정하고 메소드는 무언가를 반환해야합니다 (C # 디버거에서 허용되는 경우).


1

이것에 대한 Java 사례 (C # 사례와 매우 유사)의 세부 사항은 Java 컴파일러가 메소드가 리턴 할 수 있는지 판별하는 방법과 관련이 있습니다.

특히, 규칙은 리턴 유형이있는 메소드가 정상적으로 완료 될 수 없어야하며 대신 JLS 8.4.7에 따라 갑자기 ( 반드시 리턴 문 또는 예외를 통해 표시 함) 완료해야합니다 .

메소드가 리턴 유형을 갖도록 선언 된 경우 메소드 본문이 정상적으로 완료 될 수있는 경우 컴파일 타임 오류가 발생합니다. 다시 말해, 리턴 유형이있는 메소드는 값 리턴을 제공하는 리턴 명령문을 사용하여 리턴해야합니다. "본체의 끝을 떨어 뜨려"사용할 수 없습니다 .

컴파일러는 JLS 14.21 도달 할 수없는 명령문에 정의 된 규칙을 기반으로 정상적인 종료 가 가능한지 여부를 확인합니다 .이 규칙은 정상적인 완료를위한 규칙도 정의합니다.

특히 도달 할 수없는 명령문의 규칙은 정의 된 true상수 표현식 이있는 루프에 대해서만 특별한 경우를 만듭니다 .

while 문은 다음 중 하나 이상에 해당하면 정상적으로 완료 될 수 있습니다.

  • while 문에 도달 할 수 있고 조건식이 true 값을 갖는 상수 식 (§15.28)이 아닙니다.

  • while 문을 종료하는 도달 가능한 break 문이 있습니다.

따라서 while명령문이 정상적으로 완료 될 수 있으면 코드에 도달 할 수있는 것으로 간주되고 도달 while가능한 중단 명령문이나 상수 true표현식이 없는 루프 는 정상적으로 완료 될 수 있으므로 아래의 리턴 명령문이 필요 합니다.

이러한 규칙은 while상수 true 표현식이 있고 명령문이없는 명령문 break정상적으로 완료되는 것으로 간주되지 않으므로 그 아래의 코드는 도달 할 수없는 것으로 간주됩니다 . 메소드의 끝은 루프 아래에 있으며 루프 아래의 모든 항목에 도달 할 수 없으므로 메소드의 끝도 마찬가지이므로 메소드가 정상적으로 완료 되지 않을 수 있습니다 (이는 컴파일러가 찾는 것임).

if 반면에, 명령문은 루프에 제공되는 상수 표현식에 대한 특별한 예외가 없습니다.

비교:

// I have a compiler error!
public boolean testReturn()
{
    final boolean condition = true;

    if (condition) return true;
}

와:

// I compile just fine!
public boolean testReturn()
{
    final boolean condition = true;

    while (condition)
    {
        return true;
    }
}

구별의 이유는 매우 흥미롭고 JLS에서 컴파일러 오류를 발생시키지 않는 조건부 컴파일 플래그를 허용하려는 욕구 때문입니다.

if 문은 다음과 같은 방식으로 처리 될 것으로 예상 할 수 있습니다.

  • if-then 문은 다음 중 하나 이상에 해당하면 정상적으로 완료 될 수 있습니다.

    • if-then 문에 도달 할 수 있고 조건 표현식이 값이 true 인 상수 표현식이 아닙니다.

    • then-statement는 정상적으로 완료 될 수 있습니다.

    if-then 문에 도달 할 수 있고 조건 표현식이 값이 false 인 상수 표현식이 아닌 경우 then-statement에 도달 할 수 있습니다.

  • if-then-else 문은 then-statement가 정상적으로 완료되거나 else-statement가 정상적으로 완료 될 경우 정상적으로 완료 될 수 있습니다.

    • if-then-else 문에 도달 할 수 있고 조건 표현식이 값이 false 인 상수 표현식이 아닌 경우 then-statement에 도달 할 수 있습니다.

    • if-then-else 문에 도달 할 수 있고 조건 표현식이 값이 true 인 상수 표현식이 아닌 경우 else 문에 도달 할 수 있습니다.

이 접근법은 다른 제어 구조의 처리와 일치합니다. 그러나 if 문을 "조건부 컴파일"목적으로 편리하게 사용하려면 실제 규칙이 다릅니다.

예를 들어, 다음 명령문은 컴파일 타임 오류를 발생시킵니다.

while (false) { x=3; }그 진술 x=3;에 도달 할 수 없기 때문에 ; 그러나 피상적으로 비슷한 경우 :

if (false) { x=3; }컴파일 타임 오류가 발생하지 않습니다. 최적화 컴파일러는 명령문 x=3;이 절대로 실행되지 않으며 생성 된 클래스 파일에서 해당 명령문에 대한 코드를 생략하도록 선택할 수 있지만 명령문 x=3;은 여기에 지정된 기술적 의미에서 "도달 할 수없는"것으로 간주되지 않습니다.

이 다른 처리의 이론적 근거는 프로그래머가 다음과 같은 "플래그 변수"를 정의 할 수 있도록하는 것입니다.

static final boolean DEBUG = false; 다음과 같은 코드를 작성하십시오.

if (DEBUG) { x=3; } DEBUG의 값을 false에서 true로 또는 true에서 false로 변경 한 다음 프로그램 텍스트를 변경하지 않고 코드를 올바르게 컴파일 할 수 있어야합니다.

조건부 break 문으로 인해 컴파일러 오류가 발생하는 이유는 무엇입니까?

루프 도달 가능성 규칙에 인용 된 것처럼 while 루프는 도달 가능한 break 문이 포함 된 경우 정상적으로 완료 될 수 있습니다. if명령문의 then 절의 도달 가능성에 대한 규칙 은 전혀 조건 if을 고려 하지 않으므로 이러한 조건 if문의 then 절은 항상 도달 가능한 것으로 간주됩니다.

break에 도달 할 수 있으면 루프 뒤의 코드도 다시 도달 할 수있는 것으로 간주됩니다. 루프 후 갑자기 종료 되는 도달 가능한 코드가 없으므로 메소드는 정상적으로 완료 할 수있는 것으로 간주되므로 컴파일러는이를 오류로 플래그합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.